﻿WEBVTT

00:00:00.000 --> 00:00:06.000
Translated by visionNoob, KNU
https://github.com/insurgent92/CS231N_17_KOR_SUB

00:00:07.961 --> 00:00:12.153
잘 들리시나요? 좋습니다.
잠시 지연이 있었습니다.

00:00:12.153 --> 00:00:24.081
잠시 기술적인 문제가 있었습니다.

00:00:25.353 --> 00:00:30.420
오늘은 10강입니다.
Recurrent Neural Networks(RNN)에 대해 배워보겠습니다.

00:00:30.420 --> 00:00:33.003
그 전에 공지사항을 전달하겠습니다.

00:00:33.003 --> 00:00:37.353
현재 과제1을 채점 중에 있습니다.

00:00:37.353 --> 00:00:46.251
최종 성적이 아마도 오늘 발표될 예정입니다.
과제2 deadline 전에는 과제1 성적이 나왔으면 좋겠네요

00:00:46.251 --> 00:00:50.361
과제2의 제출기한은 금일 11:59 p.m. 까지입니다.

00:00:50.361 --> 00:00:56.633
과제 2를 다 마치신 분 있으신가요?
절반정도 되는군요

00:00:56.633 --> 00:01:03.811
제가 예전에 과제2는 상당히 오래걸릴 수 있으니
미리 시작하는 것이 좋다고 말씀드렸습니다.

00:01:03.811 --> 00:01:06.561
late dates가 남아있길 바랍니다.

00:01:06.561 --> 00:01:10.531
화요일 수업시간에는 중간고사가 있습니다.

00:01:10.531 --> 00:01:15.881
아마 강의실이 비좁아서 모두 시험을 치룰 수 없을 것입니다.

00:01:15.881 --> 00:01:20.062
따라서 다른 강의실들도 함께 빌려서 진행할 예정입니다.

00:01:20.062 --> 00:01:26.099
향후 세부사항은 다시한번 공지하겠습니다.

00:01:26.099 --> 00:01:28.179
또 한가지 공지사항이 있습니다.

00:01:28.179 --> 00:01:34.950
현재 Train Game 이라는 사이트를 개설하고있습니다.
Train Game은 웹 브라우저 기반이며

00:01:34.950 --> 00:01:39.927
여러분 누구나 접속해서 딥러닝 모델을 학습시키고
학습 도중 하이퍼파라미터를 조정하실 수 있습니다.

00:01:39.927 --> 00:01:47.646
앞서 강의들에서 배웠던 다양한 하이퍼파라미터들을
손으로 직접 만져볼 수 있는 아주 좋은 툴입니다.

00:01:47.646 --> 00:01:53.190
Train Game를 무조건 하실 필요는 없습니다.
하지만, Train Game는 실제로 다양한 데이터를 다뤄보며

00:01:53.190 --> 00:01:57.481
데이터 타입에 따라 하이퍼파라미터가 어떤 영향을 미치는지에
직관을 얻을 수 있는 아주 좋은 기회가 될것입니다.

00:01:57.481 --> 00:02:05.790
현재 몇 가지 버그를 수정하고 있습니다. 조만간 여러분에게
사용법과 함께 공개하도록 하겠습니다.

00:02:05.790 --> 00:02:11.008
다시한번 말씀드리지만, 필수는 아닙니다. 하지만 해보시면
도움이 많이 될 것입니다.

00:02:11.008 --> 00:02:17.204
Train Game을 써보신 분들에게는
Extra credit을 지급할 예정입니다.

00:02:18.208 --> 00:02:23.458
아무튼 버그만 다 잡으면
다시 한번 공지드리겠습니다.

00:02:24.720 --> 00:02:28.139
지난 강의 복습을 해봅시다.
CNN 아키텍쳐들을 배웠습니다.

00:02:28.139 --> 00:02:35.006
ImageNet Classification Challenge를
중심으로 연대순 우승자들을 알아봤습니다.

00:02:35.006 --> 00:02:41.331
2012년에는 AlexNet이 있었습니다.
9-layer CNN인데, 아주 잘 동작했습니다.

00:02:41.331 --> 00:02:48.081
AlexNet이 Computer Vision의 진화를 촉발시켰으며
딥러닝 시대의 서막을 열었습니다.

00:02:48.081 --> 00:02:56.699
2014년으로 넘어가봅시다. 이전의 모델들보다 훨씬 더
깊어진 두 모델이 있었습니다. VGG와 GoogLeNet 입니다.

00:02:56.699 --> 00:03:02.930
VGGNet은 16/19 레이어를, GoogLeNet은 22 레이어를
가진 모델이었습니다.

00:03:02.930 --> 00:03:11.230
2014년 모델들에서 흥미로운 점은 2014년에는
Batch normalization 이 발명되기 이전이라는 점입니다.

00:03:11.230 --> 00:03:18.761
Batch Norm이 없던 시절이라서 레이어가 깊을 모델을
학습시키는 일은 상당히 어려웠습습니다.

00:03:18.761 --> 00:03:24.869
이 두 모델은 깊은 모델을 수렴시키기 위해서
각종 테크닉(hackery)을 써야했습니다.

00:03:24.869 --> 00:03:28.579
가령 VGGNet는 16/19 레이어가 있습니다.

00:03:28.579 --> 00:03:34.107
하지만 처음에는 11레이어 모델을 학습시켰습니다.
11 레이어가 모델이 잘 수렴하는 한계였습니다.

00:03:34.107 --> 00:03:40.059
그리고 나서 11레이어 중간에 레이어를 무작위로 추가해서
VGG-16, VGG-19 를 학습시킨 것이죠

00:03:40.059 --> 00:03:46.539
이렇게, Bath norm이 없던 2014년에는
학습 자체가 상당히 어려운 문제였습니다.

00:03:46.539 --> 00:03:52.539
유사한 사례로 GoogLeNet의 경우에는
auxiliary classifiers를 도입했습니다.

00:03:52.539 --> 00:03:56.539
auxiliary classifiers는 classification 성능을
올리기 위해서 도입된 것이 아닙니다.

00:03:56.539 --> 00:04:03.430
단지 네트워크의 초기 레이어에 gradient를
직접 흘려보내기 위한  수단이었습니다.

00:04:03.430 --> 00:04:10.411
GoogLeNet은 Batch Norm 이전에 이런 테크닉을 사용했습니다.

00:04:10.411 --> 00:04:17.321
하지만 Batch Norm이 있다면 굳이 이런 식의
테크닉은 더이상 필요하지 않습니다.

00:04:17.321 --> 00:04:24.350
2015년에는 ResNet이라는 아주 멋드러진 모델이 있었습니다.

00:04:24.350 --> 00:04:28.310
ResNet은 shortcut connection과
residual block이 도입된 모델입니다.

00:04:28.310 --> 00:04:39.110
ResNet에서는 레이어의 출력은
입력 +  residual block의 출력 입니다.

00:04:39.110 --> 00:04:43.308
아주 흥미로운 구조입니다. 이 구조는
두 가지 아주 좋은 속성을 지니고 있습니다.

00:04:43.308 --> 00:04:49.531
하나는, 우선 residual block의 가중치가 0이면
이 block은 indentity mapping 을 합니다.

00:04:49.531 --> 00:04:55.681
이 속성은, 모델이 '필요없는 레이어" 를 사용하지 않도록
학습하는데 아주 유용합니다.

00:04:55.681 --> 00:05:02.171
그리고 ResNet의 관점에서 L2 Regularization을
해석해 볼 수도 있습니다.

00:05:02.171 --> 00:05:08.321
레이어에 L2 Regularization을 추가시키면
L2는 모든 파라미터가 0이 되도록 노력할 것입니다.

00:05:08.321 --> 00:05:12.739
사실 기본적인 CNN 아키텍쳐의 관점에서보면
모든 파라미터가 0이면 이상합니다.

00:05:12.739 --> 00:05:20.510
하지만 ResNet의 관점에서는 파라미터를 0으로 만드려는 속성은
모델이 불필요한 레이어를 사용하지 않도록 해줄 수 있습니다.

00:05:20.510 --> 00:05:26.310
파라미터들을 계속 0으로 보내면
residual block이 identity가 되기 떄문이죠.

00:05:26.310 --> 00:05:31.371
ResNet의 또 한가지 특성은 backward pass 에서의
gradient flow와 관련있습니다.

00:05:31.371 --> 00:05:34.361
"Addition gates (+)" 가 backward pass에
어떤 영향을 미칠지 생각해보면

00:05:34.361 --> 00:05:39.881
Upstream gradient 가 "Addition gate" 를 만나면
두 갈래로 나눠지게 됩니다.

00:05:39.881 --> 00:05:46.361
Upstream gradient가 오면 convolution block
으로도 흘러들어가지만

00:05:46.361 --> 00:05:50.811
residual connection 덕분에 현재 레이어를 생략하고
직접 이전 레이어로 흘러갈 수도 있습니다.

00:05:50.811 --> 00:05:59.150
Residual blocks을 수백 레이어 쌓아올린 네트워크가
있다고 해봅시다.

00:05:59.150 --> 00:06:05.561
Residual connection은 gradient를 위한 일종의
고속도로 역할을 합니다. gradient를 원활하게 전달하기 위해서죠

00:06:05.561 --> 00:06:09.630
이런 특성으로 학습을 더 쉽고 빠르게 할 수 있습니다.

00:06:09.630 --> 00:06:15.738
그리고 이런 특성은 모델이 엄청 깊더라도 잘 수렴할 수 있도록 도와줍니다.

00:06:15.738 --> 00:06:21.550
모델의 gradient flow를 잘 다루는 것은
Machine learning 전 분야에 걸쳐서도 아주 중요합니다.

00:06:21.550 --> 00:06:28.564
RNN에서도 아주 중요하죠. 따라서 gradient flow는
오늘 강의의 후반부에 다시한번 다루겠습니다.

00:06:31.148 --> 00:06:38.068
지난 시간에 DensNet, FractalNet 과 같은
이색적인 CNN 아키텍쳐도 살펴보았습니다.

00:06:38.068 --> 00:06:43.070
이 모델들은 gradient flow의 관점에서
아주 잘 해석해 볼 수 있습니다.

00:06:43.070 --> 00:06:48.619
DenseNet이나 FractalNet 같은 모델들은 모델 내부에
additional shortcut (identity)를 추가합니다.

00:06:48.619 --> 00:07:00.571
이 모델들은 Loss와 각 Layer를 직접 연결하기 때문에
 backward pass가 아주 수월합니다.

00:07:00.571 --> 00:07:09.760
CNN 아키텍쳐에서 Gradient Flow를 잘 다루려는 시도는
최근 몇 년간 아주 활발하게 이루어졌습니다.

00:07:09.760 --> 00:07:15.221
앞으로도 신기한 아카텍쳐들이 많이 나올 것 같습니다.

00:07:16.257 --> 00:07:24.331
지난 강의에서 보셨던 그래프입니다.
모델 별 연산량, 크기 등을 비교해 보았습니다.

00:07:24.331 --> 00:07:27.971
이 그래프를 유심히 살펴보면 아주 재미있는 속성이 있습니다.

00:07:27.971 --> 00:07:32.801
AlexNet와 VGGNet는 파라미터가 엄청 많습니다.

00:07:32.801 --> 00:07:37.119
사실 파라미터가 많은 이유는 전적으로
FC-layer 때문입니다.

00:07:37.119 --> 00:07:39.959
가령 AlexNet의 경우에는 대략 64M 개의 파라미터가 있습니다.

00:07:39.959 --> 00:07:47.771
AlexNet의 FC-Layer를 살펴봅시다. FC-Layers의 입력은
마지막 Conv output  6 x 6 x 256 입니다.

00:07:47.771 --> 00:07:51.190
이 값(256 x 6 x 6) 들이 4096 개의 FC-layer
노드와 연결됩니다.

00:07:51.190 --> 00:07:56.851
이 부분의 가중치 매트릭스는 엄청나게 큽니다.

00:07:56.851 --> 00:08:01.921
매트릭스의 크기는
6x6x256x4096 이 되겠죠

00:08:01.921 --> 00:08:06.370
이 레이어 하나에만 38m개의 파라미터가 있습니다.

00:08:06.370 --> 00:08:11.859
AlexNet의 전체 파라미터 중 절반 이상이
FC-Layer에 집중되어 있습니다.

00:08:11.859 --> 00:08:24.241
AlexNet의 FC-Layer 의 파라미터 갯수를 다 더해보면
FC-Layer에서만 62M 가량의 파라미터가 있다는 것을 알 수 있습니다.

00:08:24.241 --> 00:08:31.110
GoogLeNet 이나 ResNet 같은 아키텍쳐들은
FC-Layer를 상당부분 걷어냈습니다.

00:08:31.110 --> 00:08:33.698
FC-Layer를 Global Average Pooling (GAP)
으로 대체시킵니다.

00:08:33.698 --> 00:08:40.935
GAP를 사용하게 되면서 파라미터 갯수를 상당히 감소시켰습니다.

00:08:44.463 --> 00:08:49.604
지금까지는 CNN 아키텍쳐를 복습해보았습니다.

00:08:49.604 --> 00:08:56.321
이제부터는 Recurrent Neural Network에 대해서
배워보도록 하겠습니다.

00:08:56.321 --> 00:09:03.222
지금까지 배운 아키텍쳐들은(Vanilla Neural Network)
다음과 같은 모양이하고 할 수 있습니다.

00:09:03.222 --> 00:09:08.593
네트워크는 이미지 또는 벡터를 입력으로 받습니다.

00:09:08.593 --> 00:09:13.850
입력 하나가 Hidden layer를 거쳐서
하나의 출력을 내보냅니다.

00:09:13.850 --> 00:09:18.876
Classification 문제라면 카테고리가 되겠군요

00:09:20.071 --> 00:09:25.942
하지만 Machine Learning의 관점에서 생각해보면 모델이
다양한 입력을 처리할 수 있도록 유연해질 필요가 있습니다.

00:09:25.942 --> 00:09:35.313
그런 관점에서 RNN은 네트워크가 다양한 입/출력을
다룰 수 있는 여지를 제공해줍니다.

00:09:35.313 --> 00:09:41.009
RNN 구조를 이용할 때, 가령 "one to many" 모델은

00:09:41.009 --> 00:09:48.721
입력은 이미지와 같은 "단일 입력" 이지만 출력은
caption 과 같은 "가변 출력" 입니다.

00:09:48.721 --> 00:09:54.081
caption에 따라서 단어의 수가 천차 만별이겠죠
따라서 출력은 가변 출력입니다.

00:09:54.081 --> 00:09:56.491
"many to one" 모델도 있습니다.

00:09:56.491 --> 00:10:01.001
이제는 입력이 "가변 입력" 입니다.
가령 문장이 될 수 있겠군요

00:10:01.001 --> 00:10:06.161
그리고 이 문장의 "감정"을 분류하는 것입니다.
부정적/긍적적인 문장인지를 구별하는 것이죠

00:10:06.161 --> 00:10:12.512
그리고 Computer Vision Task의 경우에는, 가령 입력이
비디오라고 해봅시다. 비디오에 따라 전체 프레임 수가 다양합니다.

00:10:12.512 --> 00:10:16.401
전체 비디오를 읽으려면 가변 길이의 입력을 받아야 합니다.

00:10:16.401 --> 00:10:22.721
비디오를 입력으로 받아서, 비디오에 나타나는
activity 또는 action을 분류하는 문제를 생각해봅시다.

00:10:22.721 --> 00:10:29.931
이 경우에는 입/출력 모두 가변이어야 합니다.

00:10:29.931 --> 00:10:37.302
또는 Machine translation의 예를 볼 수도 있습니다.
입력은 English sentence 입니다. 가변 입력이죠

00:10:37.302 --> 00:10:41.633
그리고 출력은 번역 결과인 "Fench sentence" 입니다.
출력 또한 가변 출력인 것입니다.

00:10:41.633 --> 00:10:46.801
그리고 English Sentence의 길이는
변역된 French Sentence의 길이와 다를 것입니다.

00:10:46.801 --> 00:10:53.931
이 경우에는 가변 입/출력이 가능한 모델이 필요합니다.

00:10:53.931 --> 00:11:04.771
마지막으로, 입력은 비디오와 같은 가변 입력이고 각 프레임마다
출력 값이 나와야 하는 상황을 생각해 볼 수 있습니다.

00:11:04.771 --> 00:11:11.891
가령 비디오가 입력이고 매 프레임마다
classification을 해야 하는 상황일 수 있습니다.

00:11:11.891 --> 00:11:17.401
Recurrent Neural Networks는 가변 길이의 데이터를 다루기
위해 필요한 일반적인 방법(paradigm) 입니다.

00:11:17.401 --> 00:11:23.469
RNN은 앞서 말씀드린 다양한 상황들을
모델이 잘 처리할 수 있도록 줍니다.

00:11:24.349 --> 00:11:33.752
그리고 고정 길이의 입/출력이 필요한 상황에서도
RNN은 상당히 중요합니다. 여전히 유용하죠.

00:11:33.752 --> 00:11:38.793
입/출력은 고정이지만 sequential processing가
요구되는 경우입니다.

00:11:38.793 --> 00:11:46.227
가령 여기 이미지가 있습니다. 고정 입력이죠
그리고 이미지의 숫자가 몇 인지 분류하는 문제입니다.

00:11:46.227 --> 00:11:50.393
입력 이미지의 정답을 feed forward pass
한 번만 가지고 결정하는 것이 아니라

00:11:50.393 --> 00:11:55.553
이 네트워크는 이미지의 여러 부분은 조금씩 살펴봅니다.

00:11:55.553 --> 00:12:01.742
이미지를 살펴 본 후에 숫자가 몇 인지를 최종적으로 판단합니다.

00:12:01.742 --> 00:12:17.473
이 예제 처럼 입/출력이 고정된 길이라고 해도
"가변 과정(processing)" 인 경우에  RNN은 상당이 유용합니다.

00:12:17.473 --> 00:12:23.923
제가 상당히 좋아하는 논문입니다. 동일한 방법으로
"이미지를 생성" 을 제안하기도 합니다.

00:12:23.923 --> 00:12:29.723
자 이제는 모델은 Train time에서 본 이미지들을
바탕으로 새로운 이미지를 생성해 냅니다.

00:12:29.723 --> 00:12:36.254
이를 위해 RNN을 이용할 수 있습니다. 순차적으로 전체 출력의
일부분씩 생성해 내는 것입니다.

00:12:36.254 --> 00:12:46.380
이 경우에도 전체 출력은 고정된 길이지만, RNN을
이용해서 일부분씩 순차적으로 처리할 수 있습니다.

00:12:46.380 --> 00:12:51.662
RNN을 이런 경우에도 사용될 수 있는 것이죠.

00:12:51.662 --> 00:12:58.785
RNN을 활용할 수 있는 다양한 예시들을 살펴보았습니다.
그렇다면 도대체 어떻게 동작하는 것일까요?

00:12:58.785 --> 00:13:04.163
일반적으로 RNN은 작은
"Recurrent Core Cell" 을 가지고 있습니다.

00:13:04.163 --> 00:13:11.382
입력 x가 RNN으로 들어갑니다.
RNN에는 내부에 "hidden state" 를 가지고있습니다.

00:13:11.382 --> 00:13:17.641
"hidden state"는 RNN이 새로운 입력을
불러들일 때마다 매번 업데이트됩니다.

00:13:17.641 --> 00:13:23.980
"hidden state"는 모델에 feed back되고
이후에 또 다시 새로운 입력 x가 들어옵니다.

00:13:23.980 --> 00:13:28.822
RNN이 매 단계마다 값을 출력하는 경우를 생각해 봅시다.

00:13:28.822 --> 00:13:31.043
그러면 이런 식으로 진행이 됩니다.
1. RNN이 입력을 받습니다.

00:13:31.043 --> 00:13:34.469
2. "hidden state"를 업데이트합니다.
3. 출력 값을 내보냅니다.

00:13:35.814 --> 00:13:40.961
그렇다면 RNN 구조를 수식적으로 표현하면 어떨까요?

00:13:40.961 --> 00:13:46.443
오른쪽 그림의 초록색 RNN block은 "재귀적인
관계"를 연산할 수 있도록 설계됩니다. "함수 f" 로 말이죠

00:13:46.443 --> 00:13:49.094
파라미터 W를 가진 함수 f가 있습니다.

00:13:49.094 --> 00:13:55.374
함수 f는 "이전 상태의 hidden state인 h_t-1" 과
"현재 상태의 입력인 x_t" 를 입력으로 받습니다.

00:13:55.374 --> 00:14:01.420
그리고 h_t를 출력합니다. h_t는 "다음 상태의 hidden state"
입니다.(updated hidden state)

00:14:01.420 --> 00:14:11.552
그리고 다음 단계(state)에서는
h_t와 x_t+1가 입력이 됩니다.

00:14:11.552 --> 00:14:21.797
RNN에서 출력 값(y)을 가지려면 h_t 를 입력으로 하는
FC-Layer을 추가해야 합니다.

00:14:21.797 --> 00:14:27.327
FC-Layer는 매번 업데이트되는 Hidden state(h_t)를
기반으로 출력 값을 결정합니다.

00:14:27.327 --> 00:14:35.662
중요한 사실은 함수 f와 파라미터 W는
매 스텝 동일하다는 것입니다.

00:14:36.921 --> 00:14:43.434
자, RNN을 가장 간단하게 수식적으로 나타내려면
단순한(vanilla)  RNN부터 시작하는게 좋겠군요

00:14:43.434 --> 00:14:46.866
지금 보이는 수식은 앞서 있던 수식과 동일합니다.

00:14:46.866 --> 00:14:52.483
"이전 hidden state"와 "현재 입력"을 받아서
"다음 hidden state"를 출력합니다.

00:14:52.483 --> 00:15:00.124
이를 수식적으로 가장 간단하게 표현해보면,
가중치 행렬 W_xh 와 입력 x_t의 곱으로 나타낼 수 있습니다.

00:15:00.124 --> 00:15:05.615
또한 가중치 행렬 W_hh도 있습니다.
"이전 hidden state"와 곱해지는 값입니다.

00:15:05.615 --> 00:15:09.327
이렇게 두 입력(h,x) 에 대한 행렬 곱 연산이 있고
두 결과 값을 더해줍니다.

00:15:09.327 --> 00:15:13.514
그리고 System에 non-linearity를 구현하기 위해
tanh를 적용합니다.

00:15:13.514 --> 00:15:17.312
여러분 중에 왜 다른 non-linearity가 아닌
tanh를 쓰는지 궁금하신 분들도 계실 것입니다.

00:15:17.312 --> 00:15:20.594
지난 강의 동안에는 tanh가 안좋다고만 배웠는데 말이죠

00:15:20.594 --> 00:15:26.507
tanh와 관련된 문제는 lstm 배울 때
다시한번 다뤄드리도록 하겠습니다.

00:15:27.346 --> 00:15:33.394
자 이제 우리는 매 스텝마다 출력 y를 얻고 싶습니다.

00:15:33.394 --> 00:15:40.375
이를 위해서는 "hidden state"인 h_t 를
새로운 가중치 행렬 W_hy 와 곱해줍니다.

00:15:40.375 --> 00:15:44.826
가령 매 스텝에 출력 y는 class score가
될 수 있을 것입니다.

00:15:44.826 --> 00:15:51.487
Recurrent Neural Network는 크게 두 가지
방법으로 해석해 볼 수 있습니다.

00:15:51.487 --> 00:15:57.095
하나는 RNN이 hidden state를 가지며
이를 "재귀적으로" feed back 한다는 점입니다.

00:15:57.095 --> 00:16:05.914
RNN을 사실 이런 식으로 표현하면 많이 헷갈립니다.
Multiple time steps을 unrolling 해서 보면 좋습니다.

00:16:05.914 --> 00:16:11.786
Unrolling을 하면 hidden states, 입/출력, 가중치 행렬들
간의 관계를 조금 더 명확히 이해할 수 있습니다.

00:16:11.786 --> 00:16:15.494
첫 step에서는 initial hidden state인
 h_0 가 있습니다.

00:16:15.494 --> 00:16:22.415
대부분의 경우 h_0 는 0으로 초기화시킵니다.
그리고 입력 x_t가 있습니다.

00:16:22.415 --> 00:16:28.324
h_0와 x_1 이 함수 f_w의 입력으로 들어갑니다.

00:16:28.324 --> 00:16:36.154
f_w(h_0, x_1) 의 출력은 h_1 입니다. 이 과정이 반복됩니다.
이번에는 x_2 이 다음 입력으로 들어옵니다.

00:16:36.154 --> 00:16:42.847
f_w(h_1, x_2) 의 출력은 h_2가 됩니다.

00:16:42.847 --> 00:16:50.866
이 과정을 반복하면서 가변 입력 x_t 를 받습니다.

00:16:50.866 --> 00:16:58.036
이제는 조금 더 구체적인 이해를 위해서
행렬 W를 그려넣어 봅시다.

00:16:58.036 --> 00:17:03.415
여기에서 주목할 점은 "동일한 가중치 행렬 W"가
매번 사용된다는 점입니다.

00:17:03.415 --> 00:17:11.006
매번 h와 x는 달라지지만 W는 매번 동일합니다.

00:17:11.007 --> 00:17:20.786
앞서 Computational graph에서 동일한 node를 여러번
사용할 때 back prop gradient flow이 어떨지 배웠습니다.

00:17:20.786 --> 00:17:28.218
backward pass 시 dLoss/dW 를 계산하려면
행렬 W의 gradient를 전부 더해줬습니다.

00:17:28.218 --> 00:17:32.526
따라서 이 RNN 모델의 backprop을 위한
행렬 W의 그레디언트를 구하려면

00:17:32.526 --> 00:17:42.503
각 스텝에서의 W에 대한 그레디언트를 전부 계산한 뒤에
이 값들을 모두 더해주면 됩니다.

00:17:43.615 --> 00:17:47.727
Computational graph에 y_t도 넣어볼 수 있습니다.

00:17:47.727 --> 00:17:54.858
RNN의 출력 값 h_t가 또 다른 네트워크의 입력으로 들어가서
 y_t를 만들어 냅니다.

00:17:54.858 --> 00:17:59.087
가량 y_t는 매 스텝의 class score가 될 수 있겠죠

00:17:59.087 --> 00:18:00.738
RNN의 Loss도 한번 살펴봅시다.

00:18:00.738 --> 00:18:14.068
각 시퀀스마다 Ground truth label이 있다고 해봅시다. 그러면
각 스텝마다 개별적으로 y_t에 대한 Loss를 계산할 수 있습니다.

00:18:14.068 --> 00:18:22.497
여기에서 Loss는 가령 softmax loss가 될 수 있겠죠

00:18:22.497 --> 00:18:27.887
RNN의 최종 Loss는 각 개별 loss들의 합입니다.

00:18:27.887 --> 00:18:34.196
각 단계에서 Loss가 발생하면 전부 더하여
최종 네트워크 Loss를 계산합니다.

00:18:34.196 --> 00:18:42.098
이 네트워크의 Backprop을 생각해보면
모델을 학습시키려면 dLoss/dW를 구해야 합니다.

00:18:42.098 --> 00:18:46.178
Loss flowing은 각 스텝에서 이루어집니다.

00:18:46.178 --> 00:18:49.840
이 경우에는 각 스텝마다 가중치 W에 대한
local gradient를 계산할 수 있습니다.

00:18:49.840 --> 00:18:54.343
이렇게 개별로 계산된 local gradient를
최종 gradient에 더합니다.

00:18:55.597 --> 00:19:01.188
그렇다면 "many to one" 이라면 어떨까요?
가령 감정분석(sentiment analysis) 처럼 말입니다.

00:19:01.188 --> 00:19:05.799
이 경우에는 네트워크의 최종 hidden state에서만
결과 값이 나올 것입니다.

00:19:05.799 --> 00:19:11.868
최종 hidden state가 전체 시퀀스의 내용에 대한
일종의 요약으로 볼 수 있기 떄문입니다.

00:19:11.868 --> 00:19:14.788
그렇다면 "one to many" 의 경우에는 어떨까요?

00:19:14.788 --> 00:19:19.319
"고정 입력" 을 받지만  "가변 출력"인 네트워크입니다.

00:19:19.319 --> 00:19:26.050
이 경우에는 대게 고정 입력은 모델의 initial hidden state를
초기화시키는 용도로 사용합니다.

00:19:26.050 --> 00:19:30.079
그리고 RNN은 모든 스텝에서
출력 값을 가집니다.

00:19:30.079 --> 00:19:36.915
이렇게 가변 출력을 가지는 경우에도 이렇게 그래프를
Unroll 해 볼 수 있습니다.

00:19:38.490 --> 00:19:44.308
"sequence to sequent" 모델에 대해서 알아보겠습니다.
가령 machine tranlation 에 사용할 수 있는 모델입니다.

00:19:44.308 --> 00:19:47.648
"가변 입력" 과 "가변 출력" 을 가지는 모델입니다.

00:19:47.648 --> 00:19:52.398
우리는 이 모델을 "many to one" 모델과
"one to many" 모델의 결합으로 볼 수 있습니다.

00:19:52.398 --> 00:19:56.900
두 개의 스테이지로 구성되는 것이죠.
encoder와 decoder 구조입니다.

00:19:56.900 --> 00:20:02.159
encoder는 가변 입력을 받습니다.
가령 English sentence가 될 수 있겠죠

00:20:02.159 --> 00:20:08.110
그리고 encoder의 final hidden state 를 통해
전체 sentence를 요약합니다.

00:20:08.110 --> 00:20:15.769
Encoder 에서는 "many to one" 을 수행합니다.
"가변 입력"을 하나의 벡터로 요약합니다.

00:20:15.769 --> 00:20:23.111
반면 Decoder는 "one to many"를 수행합니다.
입력은 앞서 요약한 "하나의 벡터" 입니다.

00:20:23.111 --> 00:20:28.969
그리고 decoder는 "가변 출력"을 내뱉습니다.
가령 다른 언어로 변역된 문장이 될 수 있겠죠

00:20:28.969 --> 00:20:34.609
"가변 출력" 은 매 스텝 적절한 단어를 내뱉습니다.

00:20:34.609 --> 00:20:38.199
그림처럼 전체 Computational graph를 풀어서
전체 학습과정을 해석해보면

00:20:38.199 --> 00:20:44.692
Output sentence의 각 losses를 합해서
Backprob 을 진행합니다.

00:20:44.692 --> 00:20:50.940
조금 더 구체적인 예를 살펴보겠습니다. 대게는 RNN 은
Language modeling에서 자주 사용합니다.

00:20:50.940 --> 00:21:00.908
Language modeling 문제에서 하고싶은 것은 바로
"어떻게 자연어(natural lang)을 만들어낼지 입니다.

00:21:00.908 --> 00:21:06.601
가령 문자(characters) 를 내뱉는 모델이라면 매 스텝
어떻게 문자를 생성해 낼지를 풀어야 합니다.

00:21:06.601 --> 00:21:10.769
단어(word) 를 내뱉는 모델이라면 매 스텝 어떻게 단어를
생성해 낼 지를 풀어야겠죠

00:21:10.769 --> 00:21:14.740
우선은 간단한 예제를 위해서 character level
language model 을 살펴보겠습니다.

00:21:14.740 --> 00:21:22.780
네트워크는 문자열 시퀀스를 읽어드리고, 현재 문맥에서
다음 문자를 예측해야 합니다.

00:21:22.780 --> 00:21:33.884
이번 예제에서는 간단하게 단어가 [h,e,l,o] 만 있습니다.
그리고 학습시킬 문장은 "h, e, l, l, o" 입니다.

00:21:33.884 --> 00:21:49.689
Train time에서는 training sequence("hello") 의
각 단어들을 입력으로 넣어줘야 합니다.

00:21:49.689 --> 00:21:53.980
"hello" 가 RNN의 x_t입니다.

00:21:53.980 --> 00:22:01.039
우선 입력은 한 글자(letter)씩 입니다. 네트워크도
적절한 글자들을 출력해야 합니다.

00:22:01.039 --> 00:22:07.460
일반적으로 입력을 넣어주는 방법이 있습니다.
우선 vocabulary는 총 네 가지입니다.[h, e, l, o]

00:22:07.460 --> 00:22:12.589
각 글자는 하나의 벡터로 표현할 수 있습니다.
이 벡터는 1이 하나 있고 나머지는 0인 벡터입니다.

00:22:12.589 --> 00:22:16.628
벡터에서 해당 글자 위치만 1로 표시합니다.

00:22:16.628 --> 00:22:22.324
이 단순한 예제에서는 글자가  h, e, l, o 뿐입니다.
따라서 4-d 벡터로 표현할 수 있습니다.

00:22:22.324 --> 00:22:28.684
가령 h를 벡터로 표현하는 경우([1,0,0,0])
h에 해당하는 자리만 1이고 나머지는 0입니다.

00:22:28.684 --> 00:22:33.139
이런 식으로 다은 문자들도 벡터로 표현할 수 있습니다.

00:22:34.914 --> 00:22:41.874
Forward pass에서 네트워크가 어떻게 동작하는지 살펴봅시다.
우선 첫 스텝에서는, 입력 문자 'h' 가 들어옵니다.

00:22:41.874 --> 00:22:48.594
첫 번째 RNN cell로는 'h' 가 들어갑니다.
그러면 네트워크는 y_t를 출력합니다.

00:22:48.594 --> 00:22:56.024
y_t는 어떤 문자가 'h' 다음에 나올 것
같은지를 예측한 값입니다.

00:22:56.024 --> 00:23:01.405
이 예제에서는 'h'  다음에는 'e' 를 예측해야 정답입니다.

00:23:01.405 --> 00:23:06.861
하지만 현재 이 모델은 다음에 나올 글자가
'o' 라고 예측하고 있습니다.

00:23:07.850 --> 00:23:13.889
잘못 예측한 경우입니다. softmax loss가 이 예측이
얼마나 형편없는지 알려줄 것입니다.

00:23:13.889 --> 00:23:19.741
다음 스텝에서는 두 번째 단어 'e' 가 입력으로 들어갑니다.
이런 과정이 반복됩니다.

00:23:19.741 --> 00:23:27.271
우선 'e' 벡터입니다. 그리고 "이전 hidden state"와 함께
"새로운  hidden state" 를 만들어내죠

00:23:27.271 --> 00:23:31.912
이제는 두번째 hidden state를 이용해서
적절한 값을 예측해야 합니다.

00:23:31.912 --> 00:23:36.810
'hello' 를 예측해야 하므로 'e' 가
입력이라면 'l' 을 예측해야합니다.

00:23:36.810 --> 00:23:41.954
이 경우 'l' 의 예측 치가 상당히 낮기 때문에
Loss가 높습니다.

00:23:44.244 --> 00:23:50.343
이 과정을 반복합니다.
모델을 다양한 문장으로 학습시킨다면

00:23:50.343 --> 00:23:58.596
결국 모델은 이전 문장의 문맥을 참고해서 다음 문자가
무엇일지를 학습해야 할 것입니다.

00:23:59.893 --> 00:24:01.655
그렇다면 이 모델의 Test time은 어떨까요?

00:24:01.655 --> 00:24:07.594
이렇게 학습시킨 모델을 활용할 수 있는 방법들 중 하나는
Model로부터 Sampling 하는 것입니다.

00:24:07.594 --> 00:24:15.103
다시말해, Train time에 모델이 봤을 법한 문장을
모델 스스로 생성해 내는 것입니다.

00:24:15.103 --> 00:24:22.716
우선 모델에게 문장의 첫 글자만 줍니다.
이 경우에는 'h' 가 되겠군요

00:24:22.716 --> 00:24:27.295
RNN 의 첫 스텝의 입력은 'h'가 될 것입니다.

00:24:27.295 --> 00:24:32.916
'h'가 주어지면 모든 문자(h,e,l,o) 에 대한
스코어를 얻을 수 있습니다(output layer)

00:24:32.916 --> 00:24:37.501
Test time에서는 이 스코어를
"sampling"(다음 글자 선택)에 이용 합니다.

00:24:37.501 --> 00:24:41.421
스코어를 확률분포로 표현하기 위해서
sofrmax 함수를 사용할 수 있습니다.

00:24:41.421 --> 00:24:47.362
문장의 두 번째 글자를 선택하기 위해서
이 확률분포를 이용합니다.

00:24:47.362 --> 00:24:54.771
이 경우에 'e'가 뽑힐 확률(13%)이 엄청 낮았음에도
아주 운좋게 'e'가 샘플링되었습니다.

00:24:54.771 --> 00:25:02.492
이제는 확률 분포에서 운좋게 뽑힌 'e' 를
다음 스텝의 네트워크 입력으로 넣어 줄 것입니다.

00:25:02.492 --> 00:25:15.008
'e' 를 다시 벡터[0,1,0,0]로 만들어주고 다음 입력으로
넣어주면 네트워크는 두번째 출력을 만들어 냅니다.

00:25:15.008 --> 00:25:20.722
이 학습된 모델만 가지고 새로운 문장을
만들어내기 위해 이 과정을 반복합니다.

00:25:20.722 --> 00:25:27.712
전체 문장을 만들어내기 위해 타임 스텝마다
확률 분포에서 문자를 하나씩 뽑아냅니다.

00:25:27.712 --> 00:25:28.545
질문 있나요?

00:25:34.792 --> 00:25:41.315
질문은 "가장 높은 스코어를 선택하면 그만인데
왜 굳이 확률분포에서 샘플링하는지" 입니다.

00:25:41.315 --> 00:25:46.555
이 예제의 경우에는 가장 스코어가 높은 값만 사용하면
올바른 결과를 낼 수 없었습니다. (e 스코어가 낮았음)

00:25:47.451 --> 00:25:51.512
따라서 확률분포에서 샘플링을 했기 때문에
hello를 잘 만들 수 있었습니다.

00:25:51.512 --> 00:25:54.384
실제로는 두 경우 모두 사용할 수 있습니다.
(확률 분포에서 샘플링 or 그냥 가장 높은 값)

00:25:54.384 --> 00:25:59.482
어떤 경우에는 argmax probability만 사용할 수 있습니다.
(가장 높은 값을 취함) 더 안정적인 방법일 수 있습니다.

00:25:59.482 --> 00:26:04.264
하지만 확률 분포에서 샘플링하는 방법을 사용하면
일반적으로는 모델에서의 다양성을 얻을 수 있습니다.

00:26:04.264 --> 00:26:11.421
만약 항상 같은 접두사(예제에서 h)라던지, 이미지라면
같은 사진이라던지를 모델 입력으로 주면

00:26:11.421 --> 00:26:20.032
argmax을 하지 않고 샘플링을 하게되면 그럴듯한
다양한 문장을 출력할 수 있게 됩니다.

00:26:20.032 --> 00:26:23.824
첫 스텝에서 어떤 값이 샘플링되는지에
따라서 출력이 다양해 질 수 있는 것입니다.

00:26:23.824 --> 00:26:29.213
샘플링 하는 방법은 다양한 출력을 얻을 수 있다는
관점에서 좋은 방법이 될 수 있습니다.

00:26:29.213 --> 00:26:30.630
다른 질문 있나요?

00:26:35.143 --> 00:26:40.435
[학생이 질문]

00:26:46.162 --> 00:26:51.373
질문은 "Test time에 sofrmax vector를
one hot vector 대신에 넣어줄 수 있는지" 입니다.

00:26:51.373 --> 00:26:56.782
두 가지 문제가 발생할 수 있습니다. 첫 째는 그렇게 되면
입력이 Train time에서 본 입력과 달라집니다.

00:26:56.782 --> 00:27:05.413
모델에게 Train time에서 보지 못한 입력값을 주게되면
대게는 모델이 아무것도 하지 못합니다.

00:27:05.413 --> 00:27:09.112
두 번째 문제는 실제로는
vocabularies가 아주 크다는 것입니다.

00:27:09.112 --> 00:27:13.202
예제에서는 voca가 4개 뿐이 없어서
크게 문제될 것이 없겠지만

00:27:13.202 --> 00:27:18.773
한 스텝마다 단어(word)를 생성하는 모델이라고 보면
voca 안에는 이세상의 모든 영어단어가 들어가야합니다.

00:27:18.773 --> 00:27:21.162
voca는 수만개 이상의 요소를 가지게 되겠죠

00:27:21.162 --> 00:27:30.533
실제론 one hot vector를 sparse vector operation
으로 처리합니다. (dense vector가 아닌)

00:27:30.533 --> 00:27:36.827
10,000dim softmax vector를 연산해야 한다면
연산량이 어마어마 할 것입니다.

00:27:36.827 --> 00:27:40.302
Test time에서도 one hot을 사용하는 이유입니다.

00:27:42.121 --> 00:27:47.104
이런 모델의 경우 시퀀스 스텝마다 출력값이 존재합니다.

00:27:47.104 --> 00:27:51.543
이 출력값들의 Loss를 계산해 final loss를 얻는데
이를 "backpropagation through time" 라고 합니다.

00:27:51.543 --> 00:27:57.053
이 경우, forward pass의 경우에는 전체 시퀀스가
끝날 때 까지 출력값이 생성됩니다.

00:27:57.053 --> 00:28:00.762
반대로 backward pass에서도 전체 시퀀스를
가지고 Loss를 계산해야 합니다.

00:28:00.762 --> 00:28:06.162
하지만 이 경우 시퀀스가 아주 긴 경우에는
문제가 될 소지가 있습니다.

00:28:06.162 --> 00:28:15.453
가령 Wikipedia 전체 문서로 모델을 학습시킨다고 해봅시다.

00:28:15.453 --> 00:28:23.328
이 경우 학습이 정말 느릴 것입니다. gradient를 계산하려면
Wikipedia 전체 문서를 다 거쳐야 할 것입니다.

00:28:23.328 --> 00:28:27.813
Wikipedia 문서 전체에 대한 gradient를 계산하고 나면
gradient update가 1회 수행됩니다.

00:28:27.813 --> 00:28:34.172
이 과정은 아주 느릴 것입니다. 모델이 수렴될리 없겠죠.
메모리 사용량도 어마어마할 것입니다.

00:28:34.172 --> 00:28:39.933
실제로는 "truncated backpropagation" 를 통해서
backprob 을 근사시키는 기법을 사용합니다.

00:28:39.933 --> 00:28:45.562
이 방법의 아이디어는 비록 입력 스퀀스가 엄청나게
길어서 무한대라고 할지라도

00:28:45.562 --> 00:28:56.232
Train time에 한 스텝을 일정 단위로 자릅니다.
대략 100 정도로 말이죠

00:28:56.232 --> 00:29:06.261
100 스텝만 forward pass를 하고 이 서브스퀀스의
Loss를 계산합니다. 그리고 gradient step을 진행하는 것이죠.

00:29:06.261 --> 00:29:12.064
이 과정을 반복합니다. 다만 이전 batch에서 계산한
hidden states는 계속 유지합니다.

00:29:12.064 --> 00:29:20.631
다음 Batch의 forward pass를 계산할 때는
이전 hidden state를 이용합니다.

00:29:20.631 --> 00:29:28.124
그리고 gradient step은 현재 Batch에서만 진행합니다.

00:29:28.124 --> 00:29:32.760
이 방법을 "truncated backpropagation
through time" 이라고 합니다.

00:29:32.760 --> 00:29:38.250
이 과정을 반복합니다. 이전 batch에서 hidden states를
가져와서 foward pass를 수행하고

00:29:38.250 --> 00:29:43.840
backprob은 현재 배치만큼만 진행합니다.

00:29:43.840 --> 00:29:49.872
이 방법은 Stocastic gradient descent의
시퀀스 데이터 버전이라고 볼 수 있습니다.

00:29:49.872 --> 00:29:53.690
지난 시간이 Data Sets이 엄청 큰 경우 어떻게
학습시켜야 하는지 배웠습니다.

00:29:53.690 --> 00:29:58.720
Large datasets에서는 전체 데이터로 gradients를
계산하기에는 계산량이 너무 컸습니다.

00:29:58.720 --> 00:30:02.520
그래서 대신에 mini batch를 사용해서
small samples만 가지고 계산했습니다.

00:30:02.520 --> 00:30:08.053
그리고 mini batch만을 이용해서 gradient를 계산했습니다.
가령 Image classification에서 그랬었죠

00:30:08.053 --> 00:30:08.886
질문있나요?

00:30:12.441 --> 00:30:15.989
질문은 "RNN이 Markov Assumption 을 따르는지" 입니다.

00:30:15.989 --> 00:30:20.581
그렇지 않습니다. 왜냐하면 RNN은 이전 hidden state를
계속해서 앞으로 가져가기 때문입니다.(영원히)

00:30:21.442 --> 00:30:25.792
마르코 비아 가정을하고있다.
숨겨진 상태를 조건으로,

00:30:25.792 --> 00:30:31.101
그러나 숨겨진 상태가 우리가 필요로하는 모든 것입니다.
미래를 예언하기 시퀀스의

00:30:31.101 --> 00:30:35.941
하지만 그 가정은 일종의 건축입니다.
재발 성 신경 네트워크 공식 출발점에서.

00:30:35.941 --> 00:30:39.032
그리고 그것은 정말로 특별하지 않습니다.
시간을 통한 전파를 역전 시키십시오.

00:30:39.032 --> 00:30:51.239
Truncated backprop은 very large sequence
data의 gradient를 근사시키는 방법입니다.

00:30:52.677 --> 00:30:59.649
RNN을 구현하려면 코드도 엄청 복잡하고 어려울 것 같아 보이지만
사실은 그렇지 않습니다. 엄청 간결합니다.

00:30:59.649 --> 00:31:07.474
Andrej가 min-char-rnn 이라는 코드를 만들어놨습니다.
112 줄의 코드로 RNN 전체 과정을 구현하였습니다.

00:31:07.474 --> 00:31:11.725
vocabulary를 만들고, truncated back propagation
을 수행하는 모델을 구현한 코드입니다.

00:31:11.725 --> 00:31:16.584
그리고 실제 Model을 학습시키고 샘플링도 할 수 있습니다.

00:31:16.584 --> 00:31:20.862
RNN을 구성하는 것이 엄청나게 어려워 보일
수 있지만 사실은 그렇지 않습니다.

00:31:20.862 --> 00:31:27.954
슬라이드만 보면 잘 모르실 수 있습니다. 여러분이 직접
파일을 열어서 코드를 한줄 씩 살펴보시기 바랍니다.

00:31:27.954 --> 00:31:34.473
Numpy로만 작성된 dependencies가 없는
코드입니다. 코드를 읽기에도 비교적 쉽습니다.

00:31:35.584 --> 00:31:41.593
RNN Language model 로 아주 재미있는 것들을
학습시킬 수 있습니다.

00:31:41.593 --> 00:31:52.304
RNN으로 어떤 문장이건 학습시킬 수 있습니다. 인터넷에서
아무 문장이나 긁어와서 학습시킬 수 있습니다.

00:31:52.304 --> 00:32:02.634
가령 셰익스피어의 작품들을 RNN으로
학습시켜 볼 수 있습니다.

00:32:02.634 --> 00:32:11.584
학습 초기에는 모델이 의미없는 문장들만 뱉어냅니다.
하지만 학습을 시키면 시킬수록 의미있는 문장을 만들어냅니다.

00:32:11.584 --> 00:32:18.224
학습이 잘 끝나면 상당히 셰익스피어 풍의
문장들을 만들어냅니다.

00:32:18.224 --> 00:32:24.754
"Why do what that day... replied"  어쩄든
상당히 셰익스피어의 문장 처럼 보입니다.

00:32:24.754 --> 00:32:31.264
더 학습을 많이 시키면 훨씬 더 긴 문장들도
만들어낼 수 있습니다.

00:32:31.264 --> 00:32:35.864
RNN을 이용하면 이런 셰익스피어 풍의 문장과
같은 여러가지 신기한 것들을 학습시킬 수 있습니다.

00:32:35.864 --> 00:32:40.016
모델이 HEAD에 발화자를 넣어야한다는 것을 압니다.
(예 : "VIOLA:")

00:32:40.016 --> 00:32:45.565
그리고 바로 밑에 셰익스피어풍의 문장들을
만들어냅니다.

00:32:45.565 --> 00:32:47.744
다른 문장을 시작할 때 한 줄을 비워야 하는 것도 압니다.

00:32:47.744 --> 00:32:52.958
이건 엄청난 것입니다.
문장의 구조를 아주 완벽하게 학습했습니다.

00:32:52.958 --> 00:33:02.725
훨씬 더 엄청난 것도 있습니다. 인터넷에서 찾았는데요,
혹시 여기 수학 전공자가 있나요?

00:33:02.725 --> 00:33:07.325
"algebraic topology" 수업을 들어본 분이 계신가요?
몇 분정도 계시는군요

00:33:07.325 --> 00:33:15.114
여러분이 저보다 "algebraic topology"를 더 잘 하시겠네요.
제가 얼마전에 온라인"algebraic topology" 교과서를 찾았습니다.

00:33:15.114 --> 00:33:19.554
이 교과서 파일은 수학적인 내용으로 가득 차 있습니다.

00:33:19.554 --> 00:33:26.325
그리고 수식이 LaTex 문법으로 작성되어 있습니다.
LaTex을 쓰면 수식을 코드로 작성할 수 있습니다.

00:33:26.325 --> 00:33:33.146
그러면 algebraic topology 교과서의 LaTex 코드를
우리가 만든 RNN Language Model로 학습시킬 수 있습니다.

00:33:33.146 --> 00:33:41.446
자 이걸 학습시키게 되면 (웃음) 이런 식으로
algebraic topology스러운 문장들을 만들어낼 수 있습니다.

00:33:41.446 --> 00:33:46.679
모델이 수식도 만들어 냅니다.

00:33:46.679 --> 00:33:52.773
"To prrov study that F_sub U is a covering
of x prime.. blah blah" (왼쪽 4번째 문단)

00:33:52.773 --> 00:33:56.576
unions도 만들어내고, proofs의 끝에
사각형도 만들어냅니다.

00:33:56.576 --> 00:34:00.026
lemmas도 만들어내고
이전 lemmas를 참조하기도 합니다.

00:34:00.026 --> 00:34:08.417
 bi-lemma question 라던지 R 같은 기하학적인
내용도 모두 만들어냅니다. 엄청 대단하죠

00:34:09.496 --> 00:34:12.913
diagrams을 만들어내려고도 합니다.

00:34:14.239 --> 00:34:19.313
commutative diagram은 algebraic topology에서
아주 많이 사용됩니다.

00:34:19.313 --> 00:34:26.379
모델이 이 다이어그램을 열심히 만들어 내려 했지만
물론 이 다이어그램은 아무 의미가 없습니다.

00:34:26.380 --> 00:34:32.728
그리고 또 재미있는 점 중 하나는
모델이 "증명 생략" 도 학습합니다.

00:34:32.728 --> 00:34:39.694
가끔 이런 경우가 있죠.
"theorem : blah blah blah, proof : 생략"

00:34:39.695 --> 00:34:45.392
교과서에서 증명이 생각되는 부분도 모델이
정확하게 학습한 것입니다.

00:34:47.831 --> 00:34:53.497
또 다른 재미있는 것들도 할 수 있습니다. 이번에는
Linux Kernel의 전체 소스코드를 학습시켰습니다.

00:34:53.498 --> 00:34:56.801
코드를 character level로 학습시켰습니다.

00:34:56.801 --> 00:35:01.483
학습된 모델에서 샘플링을 해보면
C 소스코드 스러운 것들이 나옵니다.

00:35:01.483 --> 00:35:06.192
모델이 if 문 작성법을 알고 있습니다.
코드 짜는 스킬이 어마어마 합니다.

00:35:06.192 --> 00:35:09.552
들여쓰기와 중괄호를 쓰는법도 잘 알고있습니다.

00:35:09.552 --> 00:35:15.052
주석다는 법도 알고있습니다.
물론 뜻은 아무 의미도 없습니다.

00:35:15.052 --> 00:35:23.355
이 모델의 문제점 중 하나는. 변수를 선언해 놓고
쓰지를 않다는 것입니다.

00:35:23.355 --> 00:35:27.554
반대로 선언하지도 않은 변수를 쓰기도 합니다.
이 코드는 컴파일되지 않습니다.

00:35:27.554 --> 00:35:30.555
이 코드를 Linux에 pull request 하고싶진 않네요

00:35:30.555 --> 00:35:37.867
이 모델은 GNU license를 추가하는 법도 압니다.

00:35:37.867 --> 00:35:44.962
GNU license 다음에 include 가 나오고 include 가
몇 번 나오고 소스코드가 나옵니다.

00:35:44.962 --> 00:35:48.836
이 모델은 데이터의 일반적인 구조를
아주 잘 학습했습니다.

00:35:48.836 --> 00:35:53.398
다시 한번 말씀드립니다만, 우리가 모델에게 요청한 것은
그저 시퀀스의 다음 문자를 예측하라는 것이였습니다.

00:35:53.398 --> 00:35:55.406
구조에 대해서는 전혀 말해주지 않았죠

00:35:55.406 --> 00:36:03.355
하지만 모델은 학습과정 동안에 시퀀스 데이터의
숨겨진 구조(latent structure)를 알아서 학습합니다.

00:36:05.808 --> 00:36:10.246
결국 알아서 코드를 작성하게 된 것입니다.
아주 대단합니다.

00:36:10.246 --> 00:36:20.856
저와 Andrej가 몇 년전에 낸 논문이 있습니다. 학습한 모델이
도대체 어떤 방식으로 동작하는지 알아내고 싶었습니다.

00:36:20.856 --> 00:36:28.165
RNN에는 hidden vector가 있고 이 vector가
계속 업데이트됩니다.

00:36:28.165 --> 00:36:34.176
우리는 그 Vector를 추출해보면 해석 가능한 의미있는 것들이
나올수도 있지 않을까 추측해 보았습니다.

00:36:34.176 --> 00:36:39.917
그래서 우리는 RNN language model을
특정 dataset으로 학습시키고,

00:36:39.917 --> 00:36:47.923
그리고 hidden vector를 하나 뽑아서 어떤 값들이
들어있는지 살펴보기로 했습니다.

00:36:47.923 --> 00:36:52.206
이 서로 다른 hidden states가 도대체 어떤 것들을
예의주시하는지 알아보기 위해서였죠

00:36:52.206 --> 00:36:56.406
이 실험을 해보면, 대부분의 hidden state는
아무 의미 없는 패턴으로 예의 주시하고 있습니다.

00:36:56.406 --> 00:37:02.686
여기서 우리가 한 일은 vector 하나를 뽑은 다음에 이 시퀀스를
다시 한번 forward 시켜 보는 것입니다.

00:37:02.686 --> 00:37:10.876
여기에서 각 색깔은, 시퀀스를 읽는 동안에 앞서 뽑은
hidden vector의 값을 의미합니다.

00:37:10.876 --> 00:37:15.883
RNN의 대부분의 Cell은  해석하기 어렵습니다.

00:37:15.883 --> 00:37:21.396
대부분은 다음에 어떤 문자가 와야할 지 알아내기 위한
low level language modeling 정도로밖에 보이지 않습니다.

00:37:21.396 --> 00:37:23.438
하지만 일부는 해석해볼만 합니다.

00:37:23.438 --> 00:37:26.375
그러다가 따옴표(quotes)를 찾는 벡터를 알아냈습니다.

00:37:26.375 --> 00:37:32.486
이 vector가 하는 일을 살펴보면 처음에는 계속
off off off off ... 입니다. 파란색이죠. (파란색이 off)

00:37:32.486 --> 00:37:38.303
그러다가 따옴표를 만나면 값이 켜집니다(빨간색)
그리고 따옴표가 닫히기 전까지 유지됩니다.

00:37:39.187 --> 00:37:42.598
그러다가 두 번째 따옴표를 만나면 다시
값이 꺼집니다.

00:37:42.598 --> 00:37:48.856
이런 식으로, 그저 우리는 모델이 다음 문자를 예측하도록
학습시켰을 뿐이지만 모델은 더 유용한 것들을 학습하는 것입니다.

00:37:48.856 --> 00:37:54.222
그런 유용한 것들을 학습하기 위해서 그 Cell이
따옴표를 감지할 수 있게 된 것입니다.

00:37:54.222 --> 00:38:00.104
우리는 또, 줄 바꿈을 위해서 현재 줄의 단어 갯수를
세는 듯해 보이는 Cell도 발견했습니다.

00:38:00.104 --> 00:38:07.055
처음에는 0으로 시작했다가. 줄이 점점 길어지면
점점 빨간색으로 변합니다.

00:38:07.055 --> 00:38:11.976
점점 증가하다가 줄바꿈이 이루어지면
다시 0으로 리셋됩니다.

00:38:11.976 --> 00:38:19.545
이 Cell은 언제 모델이 new line characters을 필요로 할 지
지속적으로 추적하는 역할을 한다고 볼 수 있습니다.

00:38:19.545 --> 00:38:22.987
그리고 우리는 linux 코드를 학습시킬 때 발견한 것도 있습니다.

00:38:22.987 --> 00:38:33.838
우리는 if 문의 조건부에서 값이 켜지는 Cell을 발견했습니다.
이 Cell은 조건부의 내부와 외부를 구별할 수 있습니다.

00:38:33.838 --> 00:38:35.806
이를 통해 모델이 시퀀스를 더 잘
학습할 수 있도록 도와줍니다.

00:38:35.806 --> 00:38:44.765
그 밖에도 Comments를 찾아내거나 혹은
들여쓰기 레벨을 세는 듯한 Cell도 발견 했습니다.

00:38:44.765 --> 00:38:50.758
정말 놀라운 일입니다. 우리는 그저 다음 문자를
예측하는 모델을 학습시켰을 뿐인데

00:38:50.758 --> 00:38:55.646
결국 모델은 입력 데이터의 구조를
학습하게 되었기 때문입니다.

00:38:57.161 --> 00:39:05.528
우리는 Computer Vision 수업이니 다시 Computer Vision
에 관련된 예시로 돌아가 보겠습니다.

00:39:05.528 --> 00:39:08.747
앞서 Image Captioning Model에 대해서
몇 번 언급했었습니다.

00:39:08.747 --> 00:39:14.376
이 모델은 입력은 이미지이고
출력은 자연어로 된 Caption 입니다.

00:39:14.376 --> 00:39:19.787
이 문제를 다루는 몇몇 논문들이 있습니다.
다들 접근 방법은 비슷합니다.

00:39:19.787 --> 00:39:25.026
이 그림은 그 논문들 중 저희 연구실에서 나온 논문입니다.
아주 "공정한 기준으로?" 예시를 이 논문으로 선택헀습니다.

00:39:26.876 --> 00:39:35.608
우선 Caption은 가변길이 입니다. Caption 마다
다양한 시퀀스 길이를 가지고 있습니다.

00:39:35.608 --> 00:39:39.947
여기에 RNN Language Model가 아주 잘 어울립니다.

00:39:39.947 --> 00:39:50.379
모델에는 입력 이미지를 받기 위핸 CNN이 있습니다.
CNN은 지금까지 아주 많이 살펴봤었죠

00:39:50.379 --> 00:39:58.928
CNN은 요약된 이미지정보가 들어있는 Vector를 출력합니다.
이 Vector는 RNN의 초기 Step의 입력으로 들어갑니다.

00:39:58.928 --> 00:40:02.888
그러면 RNN은 Caption에 사용할
문자들을 하나씩 만들어냅니다.

00:40:02.888 --> 00:40:06.425
그렇다면 이 모델을 학습시킨 후에
Test time에 어떻게 동작하는지 알아봅시다.

00:40:06.425 --> 00:40:11.178
지금까지 살펴봤던 character level languate
model과 아주 유사합니다.

00:40:11.178 --> 00:40:18.638
우선 입력 이미지를 받아서 CNN의 입력으로 넣습니다.
다만 softmax scores를 사용하지 않고

00:40:18.638 --> 00:40:24.915
직전의 4,096-dim vector를 출력으로 합니다.

00:40:24.915 --> 00:40:31.377
이 vector를 이용해서 전체 이미지 정보를
요약하는데 사용할 것입니다.

00:40:31.377 --> 00:40:39.528
앞서 RNN language models에서 배웠듯이 모델이 문장을
생성해 내기에 앞서 초기 값을 넣어줘야 합니다.

00:40:39.528 --> 00:40:49.006
이 경우에는 입력이 특별합니다. 모델에게 "여기 이미지 정보가
있으니 이 조건에 맞는 문장을 만들어줘!" 라고 시작해야합니다.

00:40:49.006 --> 00:40:57.107
이전 까지의 모델에서는 RNN 모델이 두 개의 가중치 행렬을
입력으로 받았습니다. 하나는 현재 스텝의 입력이고

00:40:57.107 --> 00:41:01.240
다른 하나는 이전 스텝의 Hidden state 이었죠. 그리고
이 둘을 조합해서 다음 hidden state를 얻었습니다.

00:41:01.240 --> 00:41:04.678
하지만 이제는 이미지 정보도 더해줘야 합니다.

00:41:04.678 --> 00:41:13.105
사람마다 모델에 이미지정보를 추가하는 방법이 다르겠지만
가장 쉬운 방법은 세 번째 가중치 행렬을 추가하는 것입니다.

00:41:14.561 --> 00:41:18.598
다음 hidden state를 계산할 때마다 모든 스텝에
이 이미지 정보를 추가합니다.

00:41:18.598 --> 00:41:23.635
자 이제는 vocabulary의 모든 스코어들에 대한
분포를 계산할 차례입니다.

00:41:23.635 --> 00:41:27.307
이 문제에서 vocabulary는 "모든 영어 단어들" 과
같이 엄청나게 큽니다.

00:41:27.307 --> 00:41:32.099
그 분포에서 샘플링을 하고 그 단어를
다음 스텝의 입력으로 다시 넣어줄 것입니다.

00:41:32.099 --> 00:41:39.546
샘플링된 단어(y0)가 들어가면 다시 vacab에 대한 분포를
추정하고 다음 단어를 만들어냅니다.

00:41:39.546 --> 00:41:47.538
모든 스텝이 종료되면 결국 한 문장이 만들어집니다.

00:41:47.538 --> 00:41:54.347
이 때, &lt;END&gt; 라는 특별한 토큰이 있는데 이는
문장의 끝을 알려줍니다.

00:41:54.347 --> 00:42:00.328
&lt;END&gt; 가 샘플링되면 모델은 더이상 단어를 생성하지
않으며 이미지에 대한 caption이 완성됩니다.

00:42:00.328 --> 00:42:06.739
Train time에는 모든 caption의 종료지점에
&lt;END&gt; 토큰을 삽입합니다.

00:42:06.739 --> 00:42:10.907
네트워크가 학습하는 동안에 시퀀스의 끝에
&lt;END&gt; 토큰을 넣어야 한다는 것을 알려줘야 하기 때문입니다.

00:42:10.907 --> 00:42:16.848
학습이 끝나고 Test time에는 모델이 문장 생성을
끝마치면 &lt;END&gt; 토큰을 샘플링합니다.

00:42:16.848 --> 00:42:20.139
이 모델은 완전히 supervised learning으로 학습시킵니다.

00:42:20.139 --> 00:42:24.763
따라서 이 모델을 학습시키기 위해서는 natural
language caption이 있는 이미지를 가지고 있어야 합니다.

00:42:24.763 --> 00:42:28.666
이 Task를 위한 가장 큰 데이터셋은 아마
Microsoft COCO 데이터셋이 아닐까 싶습니다.

00:42:28.666 --> 00:42:47.837
이 모델을 학습시키기 위해서는 natural language model과
CNN을 동시에 backp 시킬 수 있습니다.

00:42:47.837 --> 00:42:52.438
이 모델을 학습시키고 나면
상당히 그럴듯한 결과를 보입니다.

00:42:52.438 --> 00:42:56.689
지금 보이는 것들이 실제 모델의 결과입니다.

00:42:56.689 --> 00:43:00.995
고양이가 여행용 가방에 앉아있습니다.
"A cat sitting on a suitcase on the floor"

00:43:00.995 --> 00:43:02.583
상당히 인상적인 결과입니다.

00:43:02.583 --> 00:43:04.975
고양이가 나뭇가지에 앉아있습니다.
"A cat is sitting on a tree branch"

00:43:04.975 --> 00:43:06.499
이 또한 상당히 놀랍습니다.

00:43:06.499 --> 00:43:09.631
두 사람이 서핑보드를 들고 해변을 걷고있습니다.
"Two people walking on the beach with surfbards"

00:43:09.631 --> 00:43:16.635
이 모델은 상당히 파워풀하고 이미지를 묘사하기 위해
비교적 복잡한 captions도 만들어낼 수 있습니다.

00:43:16.635 --> 00:43:19.808
하지만 그렇긴해도
모델이 아주 완벽하진 않습니다.

00:43:19.808 --> 00:43:24.319
전부다 마법같은 일만 일어나는 것은 아닙니다.
이 모델도 다른 Machine learning 모델들 처럼

00:43:24.319 --> 00:43:28.830
Train time에 보지 못한 데이터에 대해서는
엄청 잘 동작하지는 않습니다.

00:43:28.830 --> 00:43:33.317
가령, 여자가 손에 고양이를 들고 있습니다.
"A woman is holding a cat in her hand"

00:43:33.317 --> 00:43:35.421
하지만 이미지에 고양이는 없습니다.

00:43:35.421 --> 00:43:41.359
하지만 여자가 모피 코트를 입고있습니다. 아마도 모델이
모피 코트의 텍스쳐를 고양이로 보고 있는 것 같습니다.

00:43:41.359 --> 00:43:44.379
여자가 해변에서 서핑보드를 들고있습니다.
"A Woman standing on a beach holding a surfboard"

00:43:44.379 --> 00:43:47.892
하지만 분명 이 사람은 서핑보드를 들고있지 않습니다.
물구나무서기를 하고 있죠

00:43:47.892 --> 00:43:51.820
모델이 물구나무서기를 하고있다는 사실을 놓쳤습니다.

00:43:51.820 --> 00:43:58.238
 그리고 여기 나뭇가지에 쳐있는 거미줄 사진이 있습니다.

00:43:58.238 --> 00:44:00.382
하지만 모델은 나뭇가지에 새가 앉아있다고 생각했습니다.
"A bird is perched on a tree branch"

00:44:00.382 --> 00:44:05.139
모델은 거미에 관한 것들을 완전히 놓쳤습니다. 하지만
이 모델은 Train time에 거미를 보지 못했습니다.

00:44:05.139 --> 00:44:08.217
Train time에는 새가 나뭇가지에 않는다는 사실만
배웠을 뿐입니다.

00:44:08.217 --> 00:44:10.152
따라서 납득이 가는 실수하고 볼 수 있습니다.

00:44:10.152 --> 00:44:14.338
그리고 여기 밑에 보시면 모델은 이 사람이 공을
던지는 중인지 받는 중인지 구분하지 못합니다.

00:44:14.338 --> 00:44:17.709
하지만 어찌됐든 야구선수와 야구공이 어떤
연관이 있음은 모델이 알고 있습니다.

00:44:17.709 --> 00:44:20.441
다시한번 말씀드리지만 이 모델은
완벽하지 않습니다.

00:44:20.441 --> 00:44:25.109
Train data와 유사한 이미지를 던져주면
꾀나 잘 동작합니다.

00:44:25.109 --> 00:44:29.188
하지만 그 이상 일반적인 문제를 풀기는 조금 어렵습니다.

00:44:29.188 --> 00:44:34.152
조금 더 진보된 모델이 있습니다.
Attention 이라는 모델입니다.

00:44:34.152 --> 00:44:41.036
이 모델은 Caption을 생성할 때 이미지의 다양한 부분을
집중해서(attention) 볼 수 있습니다.

00:44:41.036 --> 00:44:44.670
이번 시간에는 Attention 모델을 깊게 다루지는 않겠습니다.

00:44:44.670 --> 00:44:48.925
어떻게 동작하는지 간단하게 말씀드리자면
우선 CNN이 있습니다.

00:44:48.925 --> 00:44:59.954
CNN로 벡터 하나를 만드는게 아니라 각 벡터가 공간정보를
가지고 있는 grid of vector를 만들어냅니다.(LxD)

00:44:59.954 --> 00:45:06.618
Forward pass시에 매 스텝 vocabulary에서
샘플링을 할 때

00:45:06.618 --> 00:45:11.263
모델이 이미지에서 보고싶은 위치에 대한 분포 또한 만들어냅니다.

00:45:11.263 --> 00:45:18.276
이미지의 각 위치에 대한 분포는 Train time에
모델이 어느 위치를 봐야하는지에 대한 attention이라 할 수 있습니다.

00:45:18.276 --> 00:45:23.155
첫 번째 hidden state(h0)는 이미지의 위치에 대한
분포를 계산합니다.

00:45:23.155 --> 00:45:31.372
이 분포(a1)를 다시 벡터 집합(LxD Feature)과 연산하여
이미지 attention(z1)을 생성합니다.

00:45:31.372 --> 00:45:36.508
이 요약된 벡터(z1)는 Neural network의
다음 스텝의 입력으로 들어갑니다.

00:45:36.508 --> 00:45:38.278
그리고 두 개의 출력이 생성됩니다. (a2, d1)

00:45:38.278 --> 00:45:43.714
하나는 vocabulary의 각 단어들의 분포입니다(d1).
그리고 다른 하나(a2)는 이미지 위치에 대한 분포입니다.

00:45:43.714 --> 00:45:49.160
이 과정을 반복하면 매 스텝마다
값 두개(a, d)가 계속 만들어집니다.

00:45:50.296 --> 00:45:58.298
Train을 끝마치면, 모델이 caption을 생성하기 위해서
이미지의 attention을 이동시키는 모습을 볼 수 있습니다.

00:45:58.298 --> 00:46:04.436
가령 여기 만들어진 caption은
"A bird flying over a body of water." 입니다.

00:46:04.436 --> 00:46:12.473
caption을 만들 때 마다 이미지 내에 다양한 곳들에
attention을 주는 것을 볼 수 있습니다.

00:46:14.112 --> 00:46:19.147
사실 여기에는 hard/soft attention의 개념이
있습니다만, 너무 깊게 들어가진 않겠습니다.

00:46:19.147 --> 00:46:26.614
soft attention의 경우에는 "모든 특징"과 "모든 이미지 위치"간의
weighted combination을 취하는 경우입니다.

00:46:26.614 --> 00:46:34.259
반면 hard attention의 경우에는 모델이 각 타임 스텝마다
단 한곳만 보도록 강제한 경우입니다.

00:46:34.259 --> 00:46:38.277
hard attention의 경우에 이미지 위치를 딱 하나만
정해야 하는데, 사실상 하나만 정하기 까다롭습니다.

00:46:38.277 --> 00:46:46.247
따라서 Hard attention을 학습시키려면 기본 backprob 보다는
조금 더 fanier한 방법을 써야만 합니다.

00:46:46.247 --> 00:46:51.498
이에 관련된 것들은 다음에
reinforcement learning 시간에 더 다루겠습니다.

00:46:51.498 --> 00:46:57.415
Attention Model을 학습시키고 나서
Caption을 생성해보면

00:46:57.415 --> 00:47:04.067
실제로 모델이 Caption을 생성할 때 의미있는 부분에
attention을 집중한다는 것을 알 수 있습니다.

00:47:04.067 --> 00:47:08.413
여자가 공원에서 원반을 던지고 있습니다. 에서
"A woman is throwing a frisbee in a park"

00:47:08.413 --> 00:47:14.516
Attention mask를 살펴보면.
모델이 "원반(frisbee)" 라는 단어를 생성할 때

00:47:14.516 --> 00:47:18.976
이미지에서도 실제 원반이 위치하는 곳을
정확하게 attention하는 것을 알 수 있습니다.

00:47:18.976 --> 00:47:24.542
엄청 대단합니다. 우리는 모델에게 매 스텝 어디를
보라고 말해준 적이 없습니다.

00:47:24.542 --> 00:47:27.955
모델 스스로가 Train time에 알아냈습니다.

00:47:27.955 --> 00:47:32.721
모델이 그쪽(원반) 영역에 집중하는 것이
올바른 일이라는 것을 스스로 알아낸 것입니다.

00:47:32.721 --> 00:47:34.610
모델 전체가 미분가능하기 때문에

00:47:34.610 --> 00:47:42.541
soft attention 또한 backprob이 가능합니다. 따라서
이 모든 것들은 train time에서 나온 것들입니다.

00:47:42.541 --> 00:47:45.297
아주 대단합니다.

00:47:45.297 --> 00:47:51.565
RNN + Attention 조합은 Image captioning 뿐만 아니라
더 다양한 것들을 할 수 있습니다.

00:47:51.565 --> 00:47:54.691
가령 Visual Question Answering(VQA) 같이 말이죠.

00:47:54.691 --> 00:48:04.010
이 문제에서는 입력이 두 가지 입니다. 하나는 이미지이고
다른 하나는 이미지에 관련된 질문입니다.

00:48:04.010 --> 00:48:10.163
왼쪽과 같은 이미지에 다음과 같은 질문을 할 수 있을 것입니다.
Q: 트럭에 그려져 있는 멸종위기 동물은 무엇입니까?

00:48:10.163 --> 00:48:19.027
모델은 네개의 보기 중에서 정답을 맞춰야 합니다.

00:48:19.027 --> 00:48:24.774
이 모델 또한 RNN과 CNN으로 만들 수 있습니다.

00:48:24.774 --> 00:48:29.701
이 경우에는 "many to one" 의 경우입니다.

00:48:29.701 --> 00:48:33.566
모델은 자연어 문장(질문)을 입력으로 받아야 합니다.

00:48:33.566 --> 00:48:40.111
이는 RNN을 통해 구현할 수 있습니다.
RNN이 질문을 vector로 요약합니다.

00:48:40.111 --> 00:48:43.928
그리고 이미지 요약을 위해서는 CNN이 필요합니다.

00:48:43.928 --> 00:48:51.645
CNN/RNN에서 나온 벡터를 조합하면 질문에 대한
분포를 예측할 수 있을 것입니다.

00:48:51.645 --> 00:48:59.175
그리고 간혹 VQA 문제를 풀기 위해서 soft special attenion
알고리즘을 적용하는 경우도 있습니다.

00:48:59.175 --> 00:49:07.062
이 예시를 보시면 모델이 정답을 결정하기 위해서
이미지에 대한 attention을 만들어내는 것을 볼 수 있습니다.

00:49:07.062 --> 00:49:09.062
질문있나요?

00:49:18.715 --> 00:49:20.878
질문은 "어떻게 서로 다른 입력을 조합하는지" 입니다.
(CNN output과 RNN output을 어떻게 합치는지)

00:49:20.878 --> 00:49:25.998
어떻게 요약된(encoded) 질문 벡터와, 요약된 이미지 벡터를
조합하는지를 여쭤보시는 것인가요?

00:49:25.998 --> 00:49:30.395
질문은 "encoded image와 encoded question를
어떻게 조합할 수 있는지" 입니다.

00:49:30.395 --> 00:49:34.847
가장 쉬운 방법중 하나는 concat으로 붙혀서
FC-Layer의 입력으로 만드는 방법입니다.

00:49:34.847 --> 00:49:37.864
이 방법이 가장 흔한 방법이고.
가장 먼저 시도해볼 수 있는 방법입니다.

00:49:37.864 --> 00:49:44.389
하지만 간혹 사람들이 더 강력한 함수를 만들기 위해서
두 벤터간의 더 복잡한 조합을 만들어내는 경우도 있습니다.

00:49:44.389 --> 00:49:48.050
어찌됐든 Concat하는 방법도 맨 처음 시도해보기에는
나쁘지 않은 방법입니다.

00:49:49.426 --> 00:49:55.084
지금까지 RNN을 활용한 다양한 예제들을 살펴보았습니다.
이 방법들 모두 대단한 방법들입니다.

00:49:55.084 --> 00:50:04.072
왜냐하면 RNN을 이용하면 CV와 NLP 문제를 조합시키는
복잡한 문제들을 풀 수 있기 때문입니다.

00:50:04.072 --> 00:50:08.870
이 복잡한 과정들은 레고 블럭들을 쌓는 것으로 볼 수 있습니다.

00:50:08.870 --> 00:50:16.736
가령 Image Captiong이나 VQA 같은 모델들은 여러
모듈(RNN,CNN)을 비교적 단순한 방법으로도 구현할 수 있습니다.

00:50:18.708 --> 00:50:26.274
또 한 가지 말씀드리고 싶은 것은, 지금까지는 단일 RNN
레이어를 사용했습니다. hidden state가 하나 뿐이었습니다.

00:50:26.274 --> 00:50:32.581
하지만 여러분이 자주 보게될 모델들은
Multi-layer RNN입니다.

00:50:32.581 --> 00:50:44.554
3-Layer RNN이 있습니다. 입력이 첫 번째 RNN으로
들어가서 첫 번째 hidden state를 만들어냅니다.

00:50:44.554 --> 00:50:50.468
RNN 하나를 돌리면 hidden states 스퀀스가 생깁니다.
(밑에서 두 번째 행)

00:50:50.468 --> 00:50:56.474
이렇게 만들어진 hidden state 시퀀스를 다른 RNN의
입력으로 넣어줄 수 있습니다.

00:50:56.474 --> 00:51:01.577
그러면 두 번째 RNN layer가 만들어내는 또 다른
hidden states 시퀀스가 생겨납니다. (밑에서 세 번째)

00:51:01.577 --> 00:51:04.181
이런 식으로 RNN layer를 쌓아올릴 수 있을 것입니다.

00:51:04.181 --> 00:51:09.398
이렇게 하는 이유는 모델이 깊어질수록 다양한 문제들에서
성능이 더 좋아지기 때문입니다.

00:51:09.398 --> 00:51:18.215
이 법칙(더 깊게 쌓는 방법)이 RNN에서도 적용됩니다.
많은 경우에 3~4 layer RNN을 사용합니다.

00:51:18.215 --> 00:51:22.086
하지만 보통은 엄청나게 싶은 RNN모델을 사용하지는 않습니다.

00:51:22.086 --> 00:51:29.327
일반적으로는 2,3,4 layer RNN이 적절합니다.

00:51:29.327 --> 00:51:38.222
RNN을 사용할 때 문제점이 있습니다.
이는 아주 흥미롭고 중요한 주제입니다.

00:51:38.222 --> 00:51:43.229
RNN을 학습시킬 때 어떤 일이 생기는지 아주
신중히 생각해 봐야 합니다.

00:51:43.229 --> 00:51:47.447
여기 지금까지 우리가 봐왔던
일반적인(vanilla) RNN Cell이 있습니다.

00:51:47.447 --> 00:51:55.307
입력은 "현재 입력, x_t" 와 "이전 hidden state h_t-1"
입니다. 그리고 이 두 입력을 쌓습니다(stack).

00:51:55.307 --> 00:52:05.783
이렇게 두 입력을 쌓고 가중치 행렬 W와 행렬 곱 연산을 하고
tanh 를 씌워서 다음 hidden state(h_t)를 만듭니다.

00:52:05.783 --> 00:52:10.131
Vanilla RNN의 기본 수식입니다.

00:52:10.131 --> 00:52:17.738
그러면 이 아키텍쳐는 backward pass에 그레디언트를
계산하는 과정에서 어떤 일이 발생할까요?

00:52:17.738 --> 00:52:27.477
우선 Backward pass 시
 h_t에 대한 loss의 미분값을 얻습니다.

00:52:28.568 --> 00:52:34.632
그 다음 Loss에 대한 h_t-1의 미분값을 계산하게 됩니다.

00:52:34.632 --> 00:52:39.881
backward pass의 전체과정은
여기 보이시는 빨간색 통로를 따르게 됩니다.

00:52:39.881 --> 00:52:45.958
우선 그레이언트가 tanh gate를 타고 흘러갑니다.
그 다음 "Mat mul gate" 를 통과합니다.

00:52:45.958 --> 00:52:54.711
여러분이 과제로 구현해 보았듯이
Mat mul gate의 backprop은

00:52:54.711 --> 00:52:58.457
결국 이 transpose(가중치 행렬) 을 곱하게 됩니다.

00:52:58.457 --> 00:53:08.024
이는 매번 vanilla RNN cells을 하나 통과할 때 마다
가중치 행렬의 일부를 곱하게 된다는 것을 의미합니다.

00:53:09.110 --> 00:53:16.599
RNN의 특성 상 RNN이 여러 시퀀스의 Cell을
쌓아 올리는 사실을 고려하면

00:53:16.599 --> 00:53:20.921
그레디언트가 RNN 모델의 Layers 시퀀스를 통해
어떤 방식으로 전달되는지 생각해 볼 수 있습니다.

00:53:20.921 --> 00:53:23.770
수상한 낌새가 보이기 시작합니다.

00:53:23.770 --> 00:53:30.810
가령 우리가 h_0에 대한 그레이언트를 구하고자 한다면
결국에는 모든 RNN Cells을 거쳐야 합니다.

00:53:30.810 --> 00:53:35.641
cell 하나를 통과할 때 마다 각 Cell의
행렬 W transpose factors가 관여합니다.

00:53:35.641 --> 00:53:44.967
다시 말해 h_0 의 그레디언트를 계산하는 식을 써보면
아주 많은 가중치 행렬들이 개입하며, 이는 안좋습니다.

00:53:44.967 --> 00:53:50.138
우선  가중치를 행렬이라고 생각하지
말고 스칼라로 생각해봅시다.

00:53:50.138 --> 00:53:54.563
우리이게 스칼라 값들이 있고 이 값들을
계속해서 곱한다고 했을 때

00:53:54.563 --> 00:54:00.269
여기 예제처럼 4개의 예제 뿐만 아니라
수백 개의 스텝이 있는 경우라면

00:54:00.269 --> 00:54:03.835
값들을 수백 번 곱해줘야 합니다. 아주 안좋습니다.

00:54:03.835 --> 00:54:14.069
만약 곱해지는 값이 1보다 큰 경우라면 점점 값이 커질 것이고
1보다 작은 경우라면 점점 작아져서 0이 될 것입니다.

00:54:14.069 --> 00:54:18.186
이 두 상황이 일어나지 않으려면
곱해지는 값이 1인 경우밖에 없습니다.

00:54:18.186 --> 00:54:20.911
실제로 1이 되기는 상당히 드뭅니다.

00:54:20.911 --> 00:54:25.229
스칼라에서의 이 intuition은 행렬에서도 동일하게 적용됩니다.

00:54:25.229 --> 00:54:32.071
가령 행렬의 특이값(singular value)이 엄청나게
큰 경우를 생각해 볼 수 있습니다.

00:54:32.071 --> 00:54:46.079
singular value가 엄청 큰 행렬을 계속 곱하는 경우에도 역시
h_0의 그레디언트는 아주 커지게됩니다.

00:54:46.079 --> 00:54:48.947
이를 "exploding gradient problem" 라고 합니다.

00:54:48.947 --> 00:54:55.061
backprop시 레이어가 깊어질수록
그레디언트가 기하급수적으로 증가하는 현상입니다.

00:54:55.061 --> 00:55:05.349
행렬의 특이값이 1보다 작은 경우라면 정 반대의 현상이 발생합니다.
이제는 기하급수적으로 그레디언트가 작아집니다.

00:55:05.349 --> 00:55:08.863
이를 "vanishing gradient problem" 이라고 합니다.

00:55:08.863 --> 00:55:14.208
그래서 사람들은 "gradient clipping" 이라는
기법을 사용하곤 합니다.

00:55:14.208 --> 00:55:19.713
"gradient clipping" 은 휴리스틱한 기법중 하나입니다.
그레디언트를 계산하고

00:55:19.713 --> 00:55:27.955
그레디언트의 L2 norm이 임계값보다 큰 경우
그레디언트가 최대 임계값을 넘지 못하도록 조정해줍니다.

00:55:27.955 --> 00:55:32.645
이 방법은 그닥 좋은 방법은 아니지만, 실제로는
많은 사람들이 RNN 학습에 이 방법을 활용합니다.

00:55:32.645 --> 00:55:39.034
exploding gradient problem을 해결하기에는
비교적 유용한 방법이긴 합니다.

00:55:39.034 --> 00:55:46.207
하지만 vanishing gradient problem를 다루려면
조금더 복잡한 RNN 아키텍쳐가 필요합니다.

00:55:46.207 --> 00:55:53.524
이는 LSTM에 관한 것입니다.
LSTM은 Long Short Term Memory라는 뜻입니다.

00:55:53.524 --> 00:55:58.316
LSTM은 RNN의 fancier 버전입니다.

00:55:58.316 --> 00:56:03.330
LSTM은 vanishing &amp; exploding gradients 문제를
완화시키기 위해서 디자인되었습니다.

00:56:03.330 --> 00:56:10.193
"gradient clipping" 같은 hack 을 쓰지 말고, 그레디언트가
잘 전달되도록 아키텍쳐 자체를 디자인한 경우입니다.

00:56:10.193 --> 00:56:16.556
기존에 배웠던 fancier CNN 아키텍쳐들과
유사한 느낌입니다.

00:56:16.556 --> 00:56:20.807
사실 LSTM은 1997년도에 나왔습니다.

00:56:20.807 --> 00:56:24.073
LSTM 이라는 아이디어 자체를 꽤 오랫동안 존재했습니다.

00:56:24.073 --> 00:56:28.852
사람들은 90년대에 이미 LSTM을 연구하고 있었고.
시대를 앞서갔던 연구라고 할 수 있습니다.

00:56:28.852 --> 00:56:32.475
20년이 지난 오늘날 LSTM은 어디서나 사용됩니다.

00:56:33.864 --> 00:56:37.921
LSTM은 아주 재밌게 생겼습니다.

00:56:37.921 --> 00:56:46.397
vanilla RNN은 hidden state가 있었습니다. 그리고 매 스텝
재귀적인 방법으로 hidden state를 업데이트했습니다.

00:56:46.397 --> 00:56:51.462
LSTM에는 한 Cell 당 두 개의 Hidden state가 있습니다.

00:56:51.462 --> 00:56:59.305
하나는 h_t 입니다. vanilla RNN에 있었던
hidden state(h_t)와 유사한 개념입니다.

00:56:59.305 --> 00:57:03.604
하지만 LSTM의 경우 "Cell state" 인 c_t라는
두 번째 벡터가 있습니다.

00:57:03.604 --> 00:57:12.240
Cell state, c_t 는 LSTM 내부에만 존재하며
밖에 노출되지 않는 변수입니다.

00:57:12.240 --> 00:57:15.260
LSTM의 Updata 식을 살펴봅시다.

00:57:15.260 --> 00:57:20.803
LSTM도 두 개의 입력을 받습니다.(h_t-1, x_t)

00:57:20.803 --> 00:57:25.485
그리고 4개의 gates를 계산합니다.
i, f, o, 그리고 g 입니다.

00:57:25.485 --> 00:57:33.802
이 gates를 cell states, c_t를 업데이트하는데 이용합니다.
그리고 c_t로 다음 스텝의 hidden state를 업데이트합니다.

00:57:36.704 --> 00:57:44.014
LSTM는 아주 흥미롭게 생겼습니다. 앞으로 몇 장의 슬라이드는
LSTM이 어떻게, 왜 동작하는지 살펴보겠습니다.

00:57:44.014 --> 00:57:47.731
vanishing or exploding gradients의 관점에서 말이죠.

00:57:47.731 --> 00:57:57.213
우선 LSTM에서는 이전 hidden state인 h_t와
현재의 입력인 x_t를 입력으로 받습니다.

00:57:57.213 --> 00:57:58.611
vanilla RNN와 동일합니다.

00:57:58.611 --> 00:58:08.663
vanilla RNN의 경우에 두 입력(h_t-1, x_t)를 Cancat하고
행렬곱 연산으로 hidden state를 직접 구했습니다.

00:58:08.663 --> 00:58:15.315
LSTM의 경우는 조금 다릅니다. 이전 hidden state와
입력을 받아서 쌓아 놓습니다.

00:58:15.315 --> 00:58:21.926
그리고 네 개의 gates의 값을 계산하기 위한
커다한 가중치 행렬을 곱해줍니다.

00:58:21.926 --> 00:58:24.391
각 gates 출력은 hidden state의 크기와 동일합니다.

00:58:24.391 --> 00:58:30.808
물론 간혹 각 gate의 가중치 행렬의 크기를
다양한 방법으로 디자인할 수도 있습니다.

00:58:30.808 --> 00:58:33.160
어떤 사람들은 모든 값들을 조합해서
하나의 큰 가중치 행렬로 만들기도 합니다.

00:58:33.160 --> 00:58:34.677
하지만 여러 방법의 기본 아이디어는 모두 같습니다.

00:58:34.677 --> 00:58:40.288
기본적으로는, h_t-1과 x_t를 입력으로 받고
gates 4개를 계산해야 합니다.

00:58:40.288 --> 00:58:47.768
gates 4개는 [i, f, o, g]인데.
기억하기 쉽게 "ifog" 라고 부르기도 합니다.

00:58:47.768 --> 00:58:53.655
I 는 input gate입니다. I는 Cell에서의 입력
x_t에 대한 가중치입니다.

00:58:53.655 --> 00:59:00.194
F는 forget gate입니다. F는 이전 스텝의 Cell의 정보를
얼마가 망강(forget) 할지에 대한 가중치입니다.

00:59:00.194 --> 00:59:04.653
O는 Output gate입니다. O는 Cell state, c_t를 얼마나
밖에 드러내 보일지에 대한 가중치입니다.

00:59:04.653 --> 00:59:10.130
G는 그닥 좋은 이름은 아니지만 저는
"gate gate" 라고 부릅니다.

00:59:10.130 --> 00:59:14.626
G는 input cell을 얼마나 포함시킬지 결정하는 가중치입니다.

00:59:14.626 --> 00:59:19.665
중요한 점 중 하나는, 각 gate에서 사용하는
nin-linearity가 각양각색 이라는 점입니다.

00:59:21.724 --> 00:59:25.047
input/forget/output gate의 경우는
sigmoid를 샤용합니다.

00:59:25.047 --> 00:59:28.571
gate의 값이 0~1 사이라는 의미입니다.

00:59:28.571 --> 00:59:34.316
반면 gate gate는 tanh를 사용합니다.
따라서 gate gate는 -1 ~ +1 의 값을 갖습니다.

00:59:34.316 --> 00:59:41.725
이 부분이 조금 이상해 보일 수도 있지만, 사실은
더 이치에 맞습니다. 가령 gate값이 binary라고 생각해 봅시다.

00:59:41.725 --> 00:59:44.744
값이 binary(0,1 or -1,+1) 이라면 어떤 일이 일어날까요?

00:59:46.033 --> 00:59:53.926
밑에 수식을 살펴봅시다.

00:59:53.926 --> 01:00:00.350
이전 스텝의 Cell states(C_t-1)는 forget gate와
element-wise multiplication 합니다.

01:00:00.350 --> 01:00:03.861
결과 vector는 (f*c_t-1) 0또는 1일 것입니다.

01:00:03.861 --> 01:00:10.644
따라서 forget gate = 0인 element는
이전 cell state를 잊습니다.

01:00:10.644 --> 01:00:14.935
반면 forget gate = 1이면 cell state의
element를 계속 기억합니다.

01:00:14.935 --> 01:00:19.767
지금까지 살펴본 forget gate는 이전 cell state의
gate on/off 를 결정했습니다.

01:00:19.767 --> 01:00:24.814
이제 두 번째 수식을 살펴보겠습니다.
element-wise product인 i * g 입니다.

01:00:24.814 --> 01:00:28.824
벡터 i의 경우에는 시그모이드에서
나온 녀석이므로 0 또는 1입니다.

01:00:28.824 --> 01:00:35.728
cell state의 각 element에 대해서, 이 cell state를
사용하고 싶으면 1 이 됩니다.

01:00:35.728 --> 01:00:41.719
반면 쓰고싶지 않으면 i = 0 이 될 것입니다.

01:00:41.719 --> 01:00:46.031
gate gate는 tanh 출력이기 때문에
값이 -1 또는 +1 입니다.

01:00:46.031 --> 01:00:54.022
c_t 는 현재 스텝에서 사용될 수 있는 "후보" 라고 할 수 있습니다.

01:00:54.022 --> 01:01:02.499
Cell State(c_t)를 계산하는 전체 수식을 살펴봅시다.이 수식은
두 개의 독립적인 scaler 값(f, i)에 의해 조정됩니다.

01:01:02.499 --> 01:01:05.230
각 값(f, i)은 1까지 증가하거나 감소합니다.

01:01:05.230 --> 01:01:11.028
c_t의 수식을 해석해보면, 우선 이전 cell state(c_t-1)을
계속 기억할지 말지를 결정합니다. (f * c_t-1)

01:01:11.028 --> 01:01:16.535
그런다음 각 스텝마다 1까지 cell state의 각 요소를
증가시키거나 감소시킬 수 있습니다. (i * g)

01:01:16.535 --> 01:01:25.708
즉 cell state의 각 요소는 scaler integer counters
처럼 값이 줄었다 늘었다 하는 것으로 볼 수 있습니다.

01:01:25.708 --> 01:01:36.513
Cell state를 계산했다면 이제는 hidden state를
업데이트 할 차례입니다. h_t는 실제 밖으로 보여지는 값입니다.

01:01:36.513 --> 01:01:43.353
그렇게 때문에 cell state는 counters의 개념으로 해석할
수 있습니다. 각 스텝마다 최대 1 또는 -1씩 세는 것이죠

01:01:43.353 --> 01:01:51.826
그리고 이 값은 tanh를 통과합니다. 그리고
최종적으로 output gate와 곱해집니다.

01:01:51.826 --> 01:01:57.597
그리고 output gate 또한 sigmoid에서 나온 값입니다.
따라서 0 ~ 1의 값을 가집니다.

01:01:57.597 --> 01:02:08.577
output gate는 각 스텝에서 다음 hidden sate를 계산할 때
cell state를 얼마나 노출시킬지를 결정합니다.

01:02:09.736 --> 01:02:17.132
대부분의 사람들이 자기들만 알아볼 수 있는 복잡한 LSTM
다이어그램을 그려놓고 남들에게 설명하려는 경향이 있습니다.

01:02:17.132 --> 01:02:18.882
그 중 이 다이어그램은 제가 만들었습니다. :D

01:02:20.380 --> 01:02:23.866
이 다이어그램을 보시면 LSTM cell이
어떤게 동작하는지 알 수 있습니다.

01:02:23.866 --> 01:02:31.266
우선 왼쪽을 보면 이전의 cell state(c_t-1)과 hidden
state(h_t-1) 을 입력으로 받습니다. 현재 입력 x_t도 있죠

01:02:31.266 --> 01:02:37.346
우선 이전 hidden state (h_t-1)과
현재 입력 x_t를 쌓습니다.

01:02:37.346 --> 01:02:41.166
그리고 가중치 행렬 W를 곱해서
4 개의 gates를 만듭니다.

01:02:41.166 --> 01:02:44.836
Non-Linearity에 관해서는 이전 슬라이드에서 다뤘으므로
여기에서는 제외시켰습니다.

01:02:44.836 --> 01:02:48.143
그리고 forget gate를
이전 cell state(c_t-1)와 곱합니다.

01:02:48.143 --> 01:02:54.524
그리고 input/gate gate가 element wise로 곱한 후
cell state(c_t-1 *f)와 곱해서 다음 cell 을 만듭니다.

01:02:54.524 --> 01:03:01.366
c_t는 tanh를 거친 후 output gate와 곱해져서
다음 hidden state(h_t)를 만들어냅니다.

01:03:02.417 --> 01:03:03.250
질문있나요?

01:03:13.116 --> 01:03:17.878
(질문 내용 X) 아닙니다. 각 gate는 각 gate마다
곱해지는 가중치 행렬을 가지고 있습니다.

01:03:17.878 --> 01:03:26.415
우선 x와 h를 쌓으면 전체 h x 1 행렬이 됩니다.

01:03:26.415 --> 01:03:30.393
그리고 가중치 행렬의 크기는
4h x 2h 입니다.

01:03:30.393 --> 01:03:41.511
이 가중치 행렬은 행렬 4개를 합쳐 놓은 것이라고 보시면 됩니다.
각 4개의 행렬은 서로 가능 gate를 계산합니다.

01:03:42.404 --> 01:03:51.109
간혹 수식를 더 간단하게 쓰기 위해서 4개의 행렬을
그저 행렬 W로 표현하는 경우도 있습니다.

01:03:51.109 --> 01:03:56.067
가중치 행렬이 합쳐있어 보이긴 해도 결국은
4개의 가중치 행렬로 나누어져 있다고 보시면 됩니다.

01:03:57.080 --> 01:04:04.574
하지만 결국은 모두 두 개의 입력(x, h) 를 쌓고
행렬 곱 연산을 수행하는 것입니다.

01:04:04.574 --> 01:04:11.196
슬라이드의 그림을 살펴보겠습니다. LSTM의
backward pass는 어떨까요?

01:04:11.196 --> 01:04:18.999
앞서 vanilla RNN의 경우에는 backward pass에서 문제점이
있었습니다. 가중치 행렬 w가 계속해서 곱해지는 문제였죠.

01:04:18.999 --> 01:04:23.238
하지만 LSTM에서는 상황이 많이 달라졌습니다.

01:04:23.238 --> 01:04:29.952
LSTM에서 cell state의 gradient를 계산하는 backward
경로를 살펴보게 되면, 아주 괜찮아보입니다.

01:04:29.952 --> 01:04:33.320
자 그럼 cell state에서 내려오는
upsteam gradient를 살펴봅시다.

01:04:33.320 --> 01:04:37.737
우선 "addition operation"의 backprob이 있습니다.

01:04:37.737 --> 01:04:43.663
지난 강의에서 배웠듯이 "addition" 에서는
upstream gradient가 그저 두 갈래로 복사됩니다.

01:04:43.663 --> 01:04:50.435
따라서 "element wise multply" 로 직접 전달됩니다.

01:04:50.435 --> 01:04:56.455
따라서 그레디언트는 upsteam gradient와
forget gate의 element wise 곱입니다.

01:04:56.455 --> 01:05:07.171
결국 Cell state의 backprob은 그저
upstream gradient * forget gate 입니다.

01:05:07.171 --> 01:05:12.640
이 특성은 vanilla RNN에 비해 좋은점이 두 가지 있습니다.

01:05:12.640 --> 01:05:18.498
우선 forget gate와 곱해지는 연산이 matrix
multiplication가 아닌 element-wise라는 점입니다.

01:05:18.498 --> 01:05:24.964
full matrix multiplication 보다는
element wise multiplication이 더 낫습니다.

01:05:24.964 --> 01:05:31.354
두 번째는 element wise multiplication을 통해 매 스텝
다른 값의 forget gate와 곱해질 수 있다는 점입니다.

01:05:31.354 --> 01:05:36.660
앞서 vanilla RNN의 경우에는 동일한
가중치 행렬(h_t)만을 계속 곱했습니다.

01:05:36.660 --> 01:05:40.563
이는 exploding/vanishing gradient문제를 일으켰습니다.

01:05:40.563 --> 01:05:45.161
반면 LSTM에서는 forget gate가 스텝마다 계속 변합니다.

01:05:45.161 --> 01:05:51.670
따라서 LSTM은 exploding/vanishing gradient
문제를 더 쉽게 해결할 수 있습니다.

01:05:51.670 --> 01:05:58.438
그리고 forget gate는 sigmoid에서 나온 값이므로
element wise multply가 0~1 사이의 값입니다.

01:05:58.438 --> 01:06:04.278
따라서 forget gate를 반복적으로 곱한다고 했을 때
더 좋은 수치적 특성을 보일 수 있습니다.

01:06:04.278 --> 01:06:14.273
그리고 또 한가지 명심해야 할 점은, vanilla RNN의 backward
pass에서는 매 스탭 그래디언트가 tanh를 거쳐야 했습니다.

01:06:14.273 --> 01:06:24.110
LSTM에서도 hidden state h_t를
출력 y_t를 계산하는 데 사용합니다.

01:06:24.110 --> 01:06:33.024
가령 LSTM의 최종 hidden state h_t를 가장 첫
cell state(c_0)까지 backprob하는 것을 생각해보면

01:06:33.024 --> 01:06:42.044
RNN처럼 매 스탭마다 tanh를 거치는 것이 아니라
tamh를 단 한번만 거치면 됩니다.

01:06:42.044 --> 01:06:50.483
LSTM을 전체적인 모습을 그려보면, Cell state를
통한 backprob은 그레디언트를 위한 고속도로라고 볼 수 있습니다.

01:06:50.483 --> 01:06:59.172
그레디언트가 모델의 종단인 Loss에서 가장 처음
Cell State(C_0)까지 흘러가면서 방해를 덜받는 것이죠

01:06:59.172 --> 01:07:00.922
질문있나요?

01:07:02.901 --> 01:07:06.792
질문은 "궁극적으로 W를 업데이트해야 할텐데,
 W에 대한 그레디언트는 어떻게 되는지" 입니다.

01:07:06.792 --> 01:07:19.848
가중치 W에 대한 Local gradient는 해당 스텝에 해당하는
현재의 cell/Hidden state로부터 전달됩니다.

01:07:19.848 --> 01:07:29.587
Vanilla RNN의 경우에는 각 스텝의 가중치
행렬 W들이 서로 영향을 미쳤습니다.

01:07:29.587 --> 01:07:37.280
반면 LSTM의 경우에는, 가령 아주 긴 시퀀스가 있고
그레디언트가 맨 끝에서부터 전달되기 시작된다고 했을 때

01:07:37.280 --> 01:07:43.219
이 시퀀스의 backprob 과정에서 각 스텝마다
w에 대한 local gradient가 있을 것입니다.

01:07:43.219 --> 01:07:48.506
그리고 W의 local gradient는
Cel/Hiddenl state에서 흘러옵니다.

01:07:48.506 --> 01:07:52.751
LSTM의 경우 cell state c가
그레디언트를 잘 전달해주기 때문에

01:07:52.751 --> 01:07:59.804
w에 대한 local gradient도 훨씬
더 깔끔하게 전달됩니다.

01:08:01.627 --> 01:08:03.044
질문 있나요?

01:08:17.428 --> 01:08:22.088
질문은 "여전히 Non-linearities가 있으므로 (sigmoid)
vanishing gradient 문제에 민감할 수 있는지" 입니다.

01:08:22.089 --> 01:08:34.103
그럴지도 모릅니다. 가령 forget gate의 경우 출력이 0~1 이니
항상 1보다 작으므로 그레디언트가 점점 감소할 수 있습니다.

01:08:34.103 --> 01:08:42.746
그래서 사람들이 많이 하는 Trick 으로 forget gate의
biases를 양수로 초기화시키는 방법이 있습니다.

01:08:42.746 --> 01:08:46.305
이 방법을 이용해서 학습 초기에 forget gate의
값이 1에 가깝도록 해줍니다.

01:08:46.305 --> 01:08:56.631
1에 가까운 값이기 때문에 적어도 학습 초기에는
그레디언트의 흐름이 비교적 원활할 것입니다.

01:08:56.631 --> 01:09:02.741
그리고 학습이 진행되면 forget gate의 biases가
적절한 자기 자리를 다시 찾아가겠죠

01:09:02.742 --> 01:09:09.182
LSTM에서도 vanishing gradient의 위험은 존재합니다.
다만 Vanilla RNN만큼 심하진 않습니다.

01:09:09.182 --> 01:09:12.126
덜 위험한 이유 중 하나는
매 스텝 f가 변하기 떄문이고

01:09:12.126 --> 01:09:19.126
두 번째 이유는 LSTM에서는 Full Mat-mul이 아닌
element-wise multiplication을 수행하기 때문입니다.

01:09:19.126 --> 01:09:23.048
LSTM을 유심히 들여다보면
RsetNet 스럽게 생겼습니다.

01:09:23.048 --> 01:09:28.069
ResNet의 Backward pass에서
identity connection이 아주 유용했습니다.

01:09:28.069 --> 01:09:32.303
Identity Mapping이 ResNet 그레디언트를
위한 고속도로 역할을 했습니다.

01:09:32.303 --> 01:09:34.924
LSTM도 동일한 intuition입니다.

01:09:34.924 --> 01:09:45.244
LSTM의 Cell state의 element-wise mult가
그레디언트를 위한 고속도로 역할을 하는 것이죠

01:09:46.343 --> 01:09:55.558
유사한 방법으로 "Highway Networks" 라는 논문도 있습니다.
LSTM cell과 ResNet의 중간 버전입니다.

01:09:57.796 --> 01:10:01.165
사실은 Highway network는
ResNet보다 일찍 나온 논문입니다.

01:10:01.165 --> 01:10:08.804
highway network는 모든 레이어에서 "candidate
activation"과 "gating function"을 계산합니다.

01:10:08.804 --> 01:10:17.017
gating function (g)는 현재 레이어의 이전 입력과 CNN 등에서
나온 candidate activation간의 보간 역할을 해줍니다.

01:10:17.017 --> 01:10:20.472
많은 아키텍쳐들(LSTM, ResNet)간의 유사성은

01:10:20.472 --> 01:10:27.688
사람들이 아주 깊은 CNN/RNN을 학습시키면서 얻은 영감에서
비롯됩니다. 이 지점에서 많은 교집합이 존재합니다.

01:10:27.688 --> 01:10:34.675
다시 말하면 실제로 아주 다양한 RNN 아키텍쳐의
변형된 버전들이 존재합니다.

01:10:34.675 --> 01:10:40.853
그 중 LSTM 다음으로 유명한 아키텍쳐는 GRU 입니다.
Gated Recurrent Unit 이죠.

01:10:40.853 --> 01:10:45.701
업데이트 식은 보시는 바와 같습니다.
LSTM과 유사하게 생겼습니다.

01:10:45.701 --> 01:10:53.828
Vanishing gradient 문제를 회피하기 위한
element-wise mult gate 등이 보이는군요

01:10:53.828 --> 01:10:59.734
그리고 또 "LSTM: A Search Space Odyssey" 라는
논문도 있습니다. 아주 독창적인 제목입니다.

01:10:59.734 --> 01:11:04.980
이 논문에서는 LSTM의 수식을 이리저리 바꾸고 실험을 합니다.
가령 non linearities를 바꿔봅니다.

01:11:04.980 --> 01:11:07.343
output gate에 꼭 tanh를 써야만 하는지
등을 실험했습니다.

01:11:07.343 --> 01:11:14.017
그들은 다양한 질문을 만들고 이에 답할 수 있는
여러가지 실험을 진행하였습니다.

01:11:14.017 --> 01:11:18.068
그리고 LSTM 모델을 조금씩 변형시켜보고
어떤 일이 발생하는지 실험하였습니다.

01:11:18.068 --> 01:11:24.521
결론은 전부 비슷비슷했다는 것입니다. 어떤 문제에서는 특정
모델이 더 잘 될수는 있겠지만. 결국은 다 비슷했습니다.

01:11:24.521 --> 01:11:32.148
하지만 그들이 시도한 어떠한 방법들도 "모든 문제들" 에서
original LSTM보다 엄청나게 좋지는 않았습니다.

01:11:32.148 --> 01:11:41.084
이는 LSTM 업데이트 수식이 뭔진 모르겠지만 어쨋든
유용하고. 각자 문제에 적용해볼만하는 것을 시사합니다.

01:11:41.084 --> 01:11:43.692
몇 해 전 Google에서도 아주 재미있는 논문이 나왔습니다.

01:11:43.692 --> 01:11:52.605
이들은 evolutionary search 기법을 이용해서 아주 많은
임의의 RNN 아키텍쳐를 실험합니다.

01:11:52.605 --> 01:12:00.860
이들은 임의로 업데이트 수식을 섞고 addition/multiplication
non-linearities를 다양한 조합으로 섞습니다.

01:12:00.860 --> 01:12:08.570
Huge Google cluster 를 이용해서 다양한 방법들을
모조리 실험해 버립니다.

01:12:08.570 --> 01:12:15.446
하지만 결론은 같습니다. GRU와 LSTM 보다 엄청나게
좋은 모델을 찾아내지는 못했습니다.

01:12:15.446 --> 01:12:19.518
조금 변형된 모델이 특정 문제에는 조금 더 좋을수는 있었습니다.
반대로 안 좋을수도 있습니다.

01:12:19.518 --> 01:12:27.080
최종 결론으로 아셔야 할 점은 바로 LSTM과 GRU가
아무렇게나 만든 수식이 아니라는 것입니다.

01:12:27.080 --> 01:12:33.356
LSTM와 GRU는 Gradient Flow를 아주 적절하게
관리하며 추가된 gate는 아주 유용함은 사실입니다.

01:12:34.888 --> 01:12:40.103
오늘 내용을 요약 하자면 RNN은 완전 멋있습니다.
RNN으로 다양한 새로운 문제에 도전해 볼 수 있습니다.

01:12:40.103 --> 01:12:43.431
떄로는 RNN은 vanishing/exploding gradient
문제에 민감할수도 있습니다.

01:12:43.431 --> 01:12:47.412
이 문제들은 weight clipping이나 다른 fancier한
아키텍쳐(LSTM, GRU)로 극복할 수 있었습니다.

01:12:47.412 --> 01:12:52.043
그리고 CNN/RNN 아키텍쳐 간에는
교집합이 아주 많았습니다.

01:12:52.043 --> 01:13:02.801
다음 시간은 중간고사합니다. 오늘 배운 RNN까지가 범위입니다.
모두 중간고사 화이팅입니다.